@intlayer/babel 7.3.1 → 7.3.2-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/cjs/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/cjs/babel-plugin-intlayer-extract.cjs +242 -0
- package/dist/cjs/babel-plugin-intlayer-extract.cjs.map +1 -0
- package/dist/cjs/{babel-plugin-intlayer.cjs → babel-plugin-intlayer-optimize.cjs} +114 -111
- package/dist/cjs/babel-plugin-intlayer-optimize.cjs.map +1 -0
- package/dist/cjs/getExtractPluginOptions.cjs +80 -0
- package/dist/cjs/getExtractPluginOptions.cjs.map +1 -0
- package/dist/cjs/getOptimizePluginOptions.cjs +60 -0
- package/dist/cjs/getOptimizePluginOptions.cjs.map +1 -0
- package/dist/cjs/index.cjs +8 -2
- package/dist/esm/_virtual/rolldown_runtime.mjs +8 -0
- package/dist/esm/babel-plugin-intlayer-extract.mjs +241 -0
- package/dist/esm/babel-plugin-intlayer-extract.mjs.map +1 -0
- package/dist/esm/{babel-plugin-intlayer.mjs → babel-plugin-intlayer-optimize.mjs} +113 -111
- package/dist/esm/babel-plugin-intlayer-optimize.mjs.map +1 -0
- package/dist/esm/getExtractPluginOptions.mjs +79 -0
- package/dist/esm/getExtractPluginOptions.mjs.map +1 -0
- package/dist/esm/getOptimizePluginOptions.mjs +59 -0
- package/dist/esm/getOptimizePluginOptions.mjs.map +1 -0
- package/dist/esm/index.mjs +5 -2
- package/dist/types/babel-plugin-intlayer-extract.d.ts +133 -0
- package/dist/types/babel-plugin-intlayer-extract.d.ts.map +1 -0
- package/dist/types/{babel-plugin-intlayer.d.ts → babel-plugin-intlayer-optimize.d.ts} +64 -55
- package/dist/types/babel-plugin-intlayer-optimize.d.ts.map +1 -0
- package/dist/types/getExtractPluginOptions.d.ts +13 -0
- package/dist/types/getExtractPluginOptions.d.ts.map +1 -0
- package/dist/types/getOptimizePluginOptions.d.ts +28 -0
- package/dist/types/getOptimizePluginOptions.d.ts.map +1 -0
- package/dist/types/index.d.ts +5 -2
- package/package.json +19 -8
- package/dist/cjs/babel-plugin-intlayer.cjs.map +0 -1
- package/dist/esm/babel-plugin-intlayer.mjs.map +0 -1
- package/dist/types/babel-plugin-intlayer.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -24,8 +24,6 @@
|
|
|
24
24
|
<a href="https://github.com/aymericzip/intlayer/blob/main/LICENSE" target="_blank" rel="noopener noreferrer nofollow"><img src="https://img.shields.io/github/license/aymericzip/intlayer?style=for-the-badge&labelColor=000000&color=FFFFFF&logoColor=000000&cacheSeconds=86400" alt="license"/></a>
|
|
25
25
|
<a href="https://github.com/aymericzip/intlayer/commits/main" target="_blank" rel="noopener noreferrer nofollow"><img src="https://img.shields.io/github/last-commit/aymericzip/intlayer?style=for-the-badge&labelColor=000000&color=FFFFFF&logoColor=000000&cacheSeconds=86400" alt="last commit"/>
|
|
26
26
|
</a>
|
|
27
|
-
<a href="https://bountyhub.dev/bounties?repo=intlayer" target="_blank" rel="noopener noreferrer nofollow"><img src="https://img.shields.io/badge/Bounties-on%20BountyHub-yellow?style=for-the-badge&labelColor=000000&color=FFFFFF&logoColor=000000&cacheSeconds=86400" alt="Bounties on BountyHub"/>
|
|
28
|
-
</a>
|
|
29
27
|
</p>
|
|
30
28
|
|
|
31
29
|

|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
|
|
29
|
+
exports.__toESM = __toESM;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
let node_path = require("node:path");
|
|
3
|
+
let __intlayer_chokidar = require("@intlayer/chokidar");
|
|
4
|
+
|
|
5
|
+
//#region src/babel-plugin-intlayer-extract.ts
|
|
6
|
+
/**
|
|
7
|
+
* Extract dictionary key from file path
|
|
8
|
+
*/
|
|
9
|
+
const extractDictionaryKeyFromPath = (filePath) => {
|
|
10
|
+
let baseName = (0, node_path.basename)(filePath, (0, node_path.extname)(filePath));
|
|
11
|
+
if (baseName === "index") baseName = (0, node_path.basename)((0, node_path.dirname)(filePath));
|
|
12
|
+
return `comp-${baseName.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase()}`;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Autonomous Babel plugin that extracts content and transforms JSX to use useIntlayer.
|
|
16
|
+
*
|
|
17
|
+
* This plugin:
|
|
18
|
+
* 1. Scans files for extractable text (JSX text, attributes)
|
|
19
|
+
* 2. Auto-injects useIntlayer import and hook call
|
|
20
|
+
* 3. Reports extracted content via onExtract callback (for the compiler to write dictionaries)
|
|
21
|
+
* 4. Replaces extractable strings with content references
|
|
22
|
+
*
|
|
23
|
+
* ## Input
|
|
24
|
+
* ```tsx
|
|
25
|
+
* export const MyComponent = () => {
|
|
26
|
+
* return <div>Hello World</div>;
|
|
27
|
+
* };
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* ## Output
|
|
31
|
+
* ```tsx
|
|
32
|
+
* import { useIntlayer } from 'react-intlayer';
|
|
33
|
+
*
|
|
34
|
+
* export const MyComponent = () => {
|
|
35
|
+
* const content = useIntlayer('comp-my-component');
|
|
36
|
+
* return <div>{content.helloWorld}</div>;
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* ## When useIntlayer is already present
|
|
41
|
+
*
|
|
42
|
+
* If the component already has a `content` variable from an existing `useIntlayer` call,
|
|
43
|
+
* the plugin will use `_compContent` to avoid naming conflicts:
|
|
44
|
+
*
|
|
45
|
+
* ### Input
|
|
46
|
+
* ```tsx
|
|
47
|
+
* export const Page = () => {
|
|
48
|
+
* const content = useIntlayer('page');
|
|
49
|
+
* return <div>{content.title} - Hello World</div>;
|
|
50
|
+
* };
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* ### Output
|
|
54
|
+
* ```tsx
|
|
55
|
+
* export const Page = () => {
|
|
56
|
+
* const _compContent = useIntlayer('comp-page');
|
|
57
|
+
* const content = useIntlayer('page');
|
|
58
|
+
* return <div>{content.title} - {_compContent.helloWorld}</div>;
|
|
59
|
+
* };
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* The extracted content is reported via the `onExtract` callback, allowing the
|
|
63
|
+
* compiler to write the dictionary to disk separately:
|
|
64
|
+
* ```json
|
|
65
|
+
* // my-component.content.json (written by compiler)
|
|
66
|
+
* {
|
|
67
|
+
* "key": "comp-my-component",
|
|
68
|
+
* "content": {
|
|
69
|
+
* "helloWorld": { "nodeType": "translation", "translation": { "en": "Hello World" } }
|
|
70
|
+
* }
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
const intlayerExtractBabelPlugin = (babel) => {
|
|
75
|
+
const { types: t } = babel;
|
|
76
|
+
return {
|
|
77
|
+
name: "babel-plugin-intlayer-extract",
|
|
78
|
+
pre() {
|
|
79
|
+
this._extractedContent = {};
|
|
80
|
+
this._existingKeys = /* @__PURE__ */ new Set();
|
|
81
|
+
this._functionsWithExtractedContent = /* @__PURE__ */ new Set();
|
|
82
|
+
this._isIncluded = true;
|
|
83
|
+
this._hasJSX = false;
|
|
84
|
+
this._hasUseIntlayerImport = false;
|
|
85
|
+
this._useIntlayerLocalName = "useIntlayer";
|
|
86
|
+
this._contentVarName = "content";
|
|
87
|
+
const filename = this.file.opts.filename;
|
|
88
|
+
if (this.opts.filesList && filename) {
|
|
89
|
+
const normalizedFilename = filename.replace(/\\/g, "/");
|
|
90
|
+
if (!this.opts.filesList.some((f) => {
|
|
91
|
+
return f.replace(/\\/g, "/") === normalizedFilename;
|
|
92
|
+
})) {
|
|
93
|
+
this._isIncluded = false;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (filename) this._dictionaryKey = extractDictionaryKeyFromPath(filename);
|
|
98
|
+
},
|
|
99
|
+
visitor: {
|
|
100
|
+
ImportDeclaration(path, state) {
|
|
101
|
+
if (!state._isIncluded) return;
|
|
102
|
+
for (const spec of path.node.specifiers) {
|
|
103
|
+
if (!t.isImportSpecifier(spec)) continue;
|
|
104
|
+
if ((t.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value) === "useIntlayer") {
|
|
105
|
+
state._hasUseIntlayerImport = true;
|
|
106
|
+
state._useIntlayerLocalName = spec.local.name;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
JSXElement(_path, state) {
|
|
111
|
+
if (!state._isIncluded) return;
|
|
112
|
+
state._hasJSX = true;
|
|
113
|
+
},
|
|
114
|
+
JSXText(path, state) {
|
|
115
|
+
if (!state._isIncluded) return;
|
|
116
|
+
const text = path.node.value;
|
|
117
|
+
if ((state.opts.shouldExtract ?? __intlayer_chokidar.shouldExtract)(text)) {
|
|
118
|
+
const key = (0, __intlayer_chokidar.generateKey)(text, state._existingKeys);
|
|
119
|
+
state._existingKeys.add(key);
|
|
120
|
+
state._extractedContent[key] = text.replace(/\s+/g, " ").trim();
|
|
121
|
+
const funcParent = path.getFunctionParent();
|
|
122
|
+
if (funcParent?.node.start != null) state._functionsWithExtractedContent.add(funcParent.node.start);
|
|
123
|
+
path.replaceWith(t.jsxExpressionContainer(t.memberExpression(t.identifier(state._contentVarName), t.identifier(key), false)));
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
JSXAttribute(path, state) {
|
|
127
|
+
if (!state._isIncluded) return;
|
|
128
|
+
const name = path.node.name;
|
|
129
|
+
if (!t.isJSXIdentifier(name)) return;
|
|
130
|
+
const attrName = name.name;
|
|
131
|
+
if (!__intlayer_chokidar.ATTRIBUTES_TO_EXTRACT.includes(attrName)) return;
|
|
132
|
+
const value = path.node.value;
|
|
133
|
+
let text = null;
|
|
134
|
+
if (t.isStringLiteral(value)) text = value.value;
|
|
135
|
+
else if (t.isJSXExpressionContainer(value) && t.isStringLiteral(value.expression)) text = value.expression.value;
|
|
136
|
+
if (text === null) return;
|
|
137
|
+
if ((state.opts.shouldExtract ?? __intlayer_chokidar.shouldExtract)(text)) {
|
|
138
|
+
const key = (0, __intlayer_chokidar.generateKey)(text, state._existingKeys);
|
|
139
|
+
state._existingKeys.add(key);
|
|
140
|
+
state._extractedContent[key] = text.trim();
|
|
141
|
+
const funcParent = path.getFunctionParent();
|
|
142
|
+
if (funcParent?.node.start != null) state._functionsWithExtractedContent.add(funcParent.node.start);
|
|
143
|
+
path.node.value = t.jsxExpressionContainer(t.memberExpression(t.memberExpression(t.identifier(state._contentVarName), t.identifier(key), false), t.identifier("value"), false));
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
Program: {
|
|
147
|
+
enter(programPath, state) {
|
|
148
|
+
if (!state._isIncluded) return;
|
|
149
|
+
let contentVarUsed = false;
|
|
150
|
+
programPath.traverse({ VariableDeclarator(varPath) {
|
|
151
|
+
if (t.isIdentifier(varPath.node.id) && varPath.node.id.name === "content") contentVarUsed = true;
|
|
152
|
+
} });
|
|
153
|
+
state._contentVarName = contentVarUsed ? "_compContent" : "content";
|
|
154
|
+
},
|
|
155
|
+
exit(programPath, state) {
|
|
156
|
+
if (!state._isIncluded) return;
|
|
157
|
+
const hasExtractedContent = Object.keys(state._extractedContent).length > 0;
|
|
158
|
+
if (!hasExtractedContent) return;
|
|
159
|
+
if (!state._hasJSX) return;
|
|
160
|
+
const defaultLocale = state.opts.defaultLocale;
|
|
161
|
+
const packageName = state.opts.packageName;
|
|
162
|
+
if (state.opts.onExtract && state._dictionaryKey && hasExtractedContent) state.opts.onExtract({
|
|
163
|
+
dictionaryKey: state._dictionaryKey,
|
|
164
|
+
filePath: state.file.opts.filename,
|
|
165
|
+
content: { ...state._extractedContent },
|
|
166
|
+
locale: defaultLocale
|
|
167
|
+
});
|
|
168
|
+
const bodyPaths = programPath.get("body");
|
|
169
|
+
if (!state._hasUseIntlayerImport) {
|
|
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
|
+
}
|
|
182
|
+
const functionsWithContent = state._functionsWithExtractedContent;
|
|
183
|
+
programPath.traverse({
|
|
184
|
+
FunctionDeclaration(funcPath) {
|
|
185
|
+
if (funcPath.node.start != null && functionsWithContent.has(funcPath.node.start)) injectHookIntoFunction(funcPath, state, t);
|
|
186
|
+
},
|
|
187
|
+
VariableDeclarator(varPath) {
|
|
188
|
+
const init = varPath.node.init;
|
|
189
|
+
if (t.isArrowFunctionExpression(init) || t.isFunctionExpression(init)) {
|
|
190
|
+
if (init.start != null && functionsWithContent.has(init.start)) injectHookIntoArrowOrExpression(varPath, init, state, t);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* Inject useIntlayer hook into a function declaration
|
|
201
|
+
*/
|
|
202
|
+
const injectHookIntoFunction = (funcPath, state, t) => {
|
|
203
|
+
const body = funcPath.node.body;
|
|
204
|
+
if (!t.isBlockStatement(body)) return;
|
|
205
|
+
let returnsJSX = false;
|
|
206
|
+
funcPath.traverse({ ReturnStatement(returnPath) {
|
|
207
|
+
const arg = returnPath.node.argument;
|
|
208
|
+
if (t.isJSXElement(arg) || t.isJSXFragment(arg)) returnsJSX = true;
|
|
209
|
+
} });
|
|
210
|
+
if (!returnsJSX) return;
|
|
211
|
+
const contentVarName = state._contentVarName;
|
|
212
|
+
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;
|
|
213
|
+
const hookCall = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._useIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
214
|
+
body.body.unshift(hookCall);
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Inject useIntlayer hook into an arrow function or function expression
|
|
218
|
+
*/
|
|
219
|
+
const injectHookIntoArrowOrExpression = (varPath, init, state, t) => {
|
|
220
|
+
const body = init.body;
|
|
221
|
+
const contentVarName = state._contentVarName;
|
|
222
|
+
if (t.isJSXElement(body) || t.isJSXFragment(body)) {
|
|
223
|
+
const hookCall$1 = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._useIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
224
|
+
const returnStmt = t.returnStatement(body);
|
|
225
|
+
init.body = t.blockStatement([hookCall$1, returnStmt]);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (!t.isBlockStatement(body)) return;
|
|
229
|
+
let returnsJSX = false;
|
|
230
|
+
varPath.traverse({ ReturnStatement(returnPath) {
|
|
231
|
+
const arg = returnPath.node.argument;
|
|
232
|
+
if (t.isJSXElement(arg) || t.isJSXFragment(arg)) returnsJSX = true;
|
|
233
|
+
} });
|
|
234
|
+
if (!returnsJSX) return;
|
|
235
|
+
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;
|
|
236
|
+
const hookCall = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._useIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
237
|
+
body.body.unshift(hookCall);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
//#endregion
|
|
241
|
+
exports.intlayerExtractBabelPlugin = intlayerExtractBabelPlugin;
|
|
242
|
+
//# sourceMappingURL=babel-plugin-intlayer-extract.cjs.map
|
|
@@ -0,0 +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,8 +1,9 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
1
2
|
let node_path = require("node:path");
|
|
2
3
|
let __intlayer_chokidar = require("@intlayer/chokidar");
|
|
3
4
|
let __intlayer_config = require("@intlayer/config");
|
|
4
5
|
|
|
5
|
-
//#region src/babel-plugin-intlayer.ts
|
|
6
|
+
//#region src/babel-plugin-intlayer-optimize.ts
|
|
6
7
|
const PACKAGE_LIST = [
|
|
7
8
|
"intlayer",
|
|
8
9
|
"@intlayer/core",
|
|
@@ -140,7 +141,7 @@ const computeImport = (fromFile, dictionariesDir, dynamicDictionariesDir, fetchD
|
|
|
140
141
|
* const content2 = getIntlayer(_dicHash);
|
|
141
142
|
* ```
|
|
142
143
|
*/
|
|
143
|
-
const
|
|
144
|
+
const intlayerOptimizeBabelPlugin = (babel) => {
|
|
144
145
|
const { types: t } = babel;
|
|
145
146
|
return {
|
|
146
147
|
name: "babel-plugin-intlayer-transform",
|
|
@@ -151,6 +152,10 @@ const intlayerBabelPlugin = (babel) => {
|
|
|
151
152
|
this._hasValidImport = false;
|
|
152
153
|
this._isDictEntry = false;
|
|
153
154
|
this._useDynamicHelpers = false;
|
|
155
|
+
if (this.opts.optimize === false) {
|
|
156
|
+
this._isIncluded = false;
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
154
159
|
const filename = this.file.opts.filename;
|
|
155
160
|
if (this.opts.filesList && filename) {
|
|
156
161
|
if (!this.opts.filesList.includes(filename)) {
|
|
@@ -159,123 +164,121 @@ const intlayerBabelPlugin = (babel) => {
|
|
|
159
164
|
}
|
|
160
165
|
}
|
|
161
166
|
},
|
|
162
|
-
visitor: {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
exit(programPath, state) {
|
|
179
|
-
if (state._isDictEntry) return;
|
|
180
|
-
if (!state._hasValidImport) return;
|
|
181
|
-
if (!state._isIncluded) return;
|
|
182
|
-
const file = state.file.opts.filename;
|
|
183
|
-
const dictionariesDir = state.opts.dictionariesDir;
|
|
184
|
-
const dynamicDictionariesDir = state.opts.dynamicDictionariesDir;
|
|
185
|
-
const fetchDictionariesDir = state.opts.fetchDictionariesDir;
|
|
186
|
-
const imports = [];
|
|
187
|
-
for (const [key, ident] of state._newStaticImports) {
|
|
188
|
-
const rel = computeImport(file, dictionariesDir, dynamicDictionariesDir, fetchDictionariesDir, key, "static");
|
|
189
|
-
const importDeclarationNode = t.importDeclaration([t.importDefaultSpecifier(t.identifier(ident.name))], t.stringLiteral(rel));
|
|
190
|
-
importDeclarationNode.attributes = [t.importAttribute(t.identifier("type"), t.stringLiteral("json"))];
|
|
191
|
-
imports.push(importDeclarationNode);
|
|
192
|
-
}
|
|
193
|
-
for (const [key, ident] of state._newDynamicImports) {
|
|
194
|
-
const rel = computeImport(file, dictionariesDir, dynamicDictionariesDir, fetchDictionariesDir, key, ident.name.endsWith("_fetch") ? "live" : "dynamic");
|
|
195
|
-
imports.push(t.importDeclaration([t.importDefaultSpecifier(t.identifier(ident.name))], t.stringLiteral(rel)));
|
|
196
|
-
}
|
|
197
|
-
if (!imports.length) return;
|
|
198
|
-
const bodyPaths = programPath.get("body");
|
|
199
|
-
let insertPos = 0;
|
|
200
|
-
for (const stmtPath of bodyPaths) {
|
|
201
|
-
const stmt = stmtPath.node;
|
|
202
|
-
if (t.isExpressionStatement(stmt) && t.isStringLiteral(stmt.expression) && !stmt.expression.value.startsWith("import") && !stmt.expression.value.startsWith("require")) insertPos += 1;
|
|
203
|
-
else break;
|
|
204
|
-
}
|
|
205
|
-
programPath.node.body.splice(insertPos, 0, ...imports);
|
|
167
|
+
visitor: { Program: {
|
|
168
|
+
enter(programPath, state) {
|
|
169
|
+
const filename = state.file.opts.filename;
|
|
170
|
+
if (state.opts.replaceDictionaryEntry && filename === state.opts.dictionariesEntryPath) {
|
|
171
|
+
state._isDictEntry = true;
|
|
172
|
+
programPath.traverse({
|
|
173
|
+
ImportDeclaration(path) {
|
|
174
|
+
path.remove();
|
|
175
|
+
},
|
|
176
|
+
VariableDeclarator(path) {
|
|
177
|
+
if (t.isObjectExpression(path.node.init)) path.node.init.properties = [];
|
|
178
|
+
}
|
|
179
|
+
});
|
|
206
180
|
}
|
|
207
181
|
},
|
|
208
|
-
|
|
182
|
+
exit(programPath, state) {
|
|
209
183
|
if (state._isDictEntry) return;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
184
|
+
if (!state._isIncluded) return;
|
|
185
|
+
programPath.traverse({
|
|
186
|
+
ImportDeclaration(path) {
|
|
187
|
+
const src = path.node.source.value;
|
|
188
|
+
if (!PACKAGE_LIST.includes(src)) return;
|
|
189
|
+
state._hasValidImport = true;
|
|
190
|
+
for (const spec of path.node.specifiers) {
|
|
191
|
+
if (!t.isImportSpecifier(spec)) continue;
|
|
192
|
+
const importedName = t.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value;
|
|
193
|
+
const importMode = state.opts.importMode;
|
|
194
|
+
const shouldUseDynamicHelpers = (importMode === "dynamic" || importMode === "live") && PACKAGE_LIST_DYNAMIC.includes(src);
|
|
195
|
+
if (shouldUseDynamicHelpers) state._useDynamicHelpers = true;
|
|
196
|
+
let helperMap;
|
|
197
|
+
if (shouldUseDynamicHelpers) helperMap = {
|
|
198
|
+
...STATIC_IMPORT_FUNCTION,
|
|
199
|
+
...DYNAMIC_IMPORT_FUNCTION
|
|
200
|
+
};
|
|
201
|
+
else helperMap = STATIC_IMPORT_FUNCTION;
|
|
202
|
+
const newIdentifier = helperMap[importedName];
|
|
203
|
+
if (newIdentifier) spec.imported = t.identifier(newIdentifier);
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
CallExpression(path) {
|
|
207
|
+
const callee = path.node.callee;
|
|
208
|
+
if (!t.isIdentifier(callee)) return;
|
|
209
|
+
if (!CALLER_LIST.includes(callee.name)) return;
|
|
210
|
+
state._hasValidImport = true;
|
|
211
|
+
const arg = path.node.arguments[0];
|
|
212
|
+
if (!arg || !t.isStringLiteral(arg)) return;
|
|
213
|
+
const key = arg.value;
|
|
214
|
+
const importMode = state.opts.importMode;
|
|
215
|
+
const isUseIntlayer = callee.name === "useIntlayer";
|
|
216
|
+
const useDynamicHelpers = Boolean(state._useDynamicHelpers);
|
|
217
|
+
let perCallMode = "static";
|
|
218
|
+
if (isUseIntlayer && useDynamicHelpers) {
|
|
219
|
+
if (importMode === "dynamic") perCallMode = "dynamic";
|
|
220
|
+
else if (importMode === "live") perCallMode = (state.opts.liveSyncKeys ?? []).includes(key) ? "live" : "dynamic";
|
|
221
|
+
}
|
|
222
|
+
let ident;
|
|
223
|
+
if (perCallMode === "live") {
|
|
224
|
+
let dynamicIdent = state._newDynamicImports?.get(key);
|
|
225
|
+
if (!dynamicIdent) {
|
|
226
|
+
const hash = (0, __intlayer_chokidar.getFileHash)(key);
|
|
227
|
+
dynamicIdent = t.identifier(`_${hash}_fetch`);
|
|
228
|
+
state._newDynamicImports?.set(key, dynamicIdent);
|
|
229
|
+
}
|
|
230
|
+
ident = dynamicIdent;
|
|
231
|
+
path.node.arguments = [t.identifier(ident.name), ...path.node.arguments];
|
|
232
|
+
} else if (perCallMode === "dynamic") {
|
|
233
|
+
let dynamicIdent = state._newDynamicImports?.get(key);
|
|
234
|
+
if (!dynamicIdent) {
|
|
235
|
+
const hash = (0, __intlayer_chokidar.getFileHash)(key);
|
|
236
|
+
dynamicIdent = t.identifier(`_${hash}_dyn`);
|
|
237
|
+
state._newDynamicImports?.set(key, dynamicIdent);
|
|
238
|
+
}
|
|
239
|
+
ident = dynamicIdent;
|
|
240
|
+
path.node.arguments = [t.identifier(ident.name), ...path.node.arguments];
|
|
241
|
+
} else {
|
|
242
|
+
let staticIdent = state._newStaticImports?.get(key);
|
|
243
|
+
if (!staticIdent) {
|
|
244
|
+
staticIdent = makeIdent(key, t);
|
|
245
|
+
state._newStaticImports?.set(key, staticIdent);
|
|
246
|
+
}
|
|
247
|
+
ident = staticIdent;
|
|
248
|
+
path.node.arguments[0] = t.identifier(ident.name);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
if (!state._hasValidImport) return;
|
|
253
|
+
const file = state.file.opts.filename;
|
|
254
|
+
const dictionariesDir = state.opts.dictionariesDir;
|
|
255
|
+
const dynamicDictionariesDir = state.opts.dynamicDictionariesDir;
|
|
256
|
+
const fetchDictionariesDir = state.opts.fetchDictionariesDir;
|
|
257
|
+
const imports = [];
|
|
258
|
+
for (const [key, ident] of state._newStaticImports) {
|
|
259
|
+
const rel = computeImport(file, dictionariesDir, dynamicDictionariesDir, fetchDictionariesDir, key, "static");
|
|
260
|
+
const importDeclarationNode = t.importDeclaration([t.importDefaultSpecifier(t.identifier(ident.name))], t.stringLiteral(rel));
|
|
261
|
+
importDeclarationNode.attributes = [t.importAttribute(t.identifier("type"), t.stringLiteral("json"))];
|
|
262
|
+
imports.push(importDeclarationNode);
|
|
227
263
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const callee = path.node.callee;
|
|
232
|
-
if (!t.isIdentifier(callee)) return;
|
|
233
|
-
if (!CALLER_LIST.includes(callee.name)) return;
|
|
234
|
-
state._hasValidImport = true;
|
|
235
|
-
const arg = path.node.arguments[0];
|
|
236
|
-
if (!arg || !t.isStringLiteral(arg)) return;
|
|
237
|
-
const key = arg.value;
|
|
238
|
-
const importMode = state.opts.importMode;
|
|
239
|
-
const isUseIntlayer = callee.name === "useIntlayer";
|
|
240
|
-
const useDynamicHelpers = Boolean(state._useDynamicHelpers);
|
|
241
|
-
let perCallMode = "static";
|
|
242
|
-
if (isUseIntlayer && useDynamicHelpers) {
|
|
243
|
-
if (importMode === "dynamic") perCallMode = "dynamic";
|
|
244
|
-
else if (importMode === "live") perCallMode = (state.opts.liveSyncKeys ?? []).includes(key) ? "live" : "dynamic";
|
|
264
|
+
for (const [key, ident] of state._newDynamicImports) {
|
|
265
|
+
const rel = computeImport(file, dictionariesDir, dynamicDictionariesDir, fetchDictionariesDir, key, ident.name.endsWith("_fetch") ? "live" : "dynamic");
|
|
266
|
+
imports.push(t.importDeclaration([t.importDefaultSpecifier(t.identifier(ident.name))], t.stringLiteral(rel)));
|
|
245
267
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
ident = dynamicIdent;
|
|
255
|
-
path.node.arguments = [t.identifier(ident.name), ...path.node.arguments];
|
|
256
|
-
} else if (perCallMode === "dynamic") {
|
|
257
|
-
let dynamicIdent = state._newDynamicImports?.get(key);
|
|
258
|
-
if (!dynamicIdent) {
|
|
259
|
-
const hash = (0, __intlayer_chokidar.getFileHash)(key);
|
|
260
|
-
dynamicIdent = t.identifier(`_${hash}_dyn`);
|
|
261
|
-
state._newDynamicImports?.set(key, dynamicIdent);
|
|
262
|
-
}
|
|
263
|
-
ident = dynamicIdent;
|
|
264
|
-
path.node.arguments = [t.identifier(ident.name), ...path.node.arguments];
|
|
265
|
-
} else {
|
|
266
|
-
let staticIdent = state._newStaticImports?.get(key);
|
|
267
|
-
if (!staticIdent) {
|
|
268
|
-
staticIdent = makeIdent(key, t);
|
|
269
|
-
state._newStaticImports?.set(key, staticIdent);
|
|
270
|
-
}
|
|
271
|
-
ident = staticIdent;
|
|
272
|
-
path.node.arguments[0] = t.identifier(ident.name);
|
|
268
|
+
if (!imports.length) return;
|
|
269
|
+
const bodyPaths = programPath.get("body");
|
|
270
|
+
let insertPos = 0;
|
|
271
|
+
for (const stmtPath of bodyPaths) {
|
|
272
|
+
const stmt = stmtPath.node;
|
|
273
|
+
if (t.isExpressionStatement(stmt) && t.isStringLiteral(stmt.expression) && !stmt.expression.value.startsWith("import") && !stmt.expression.value.startsWith("require")) insertPos += 1;
|
|
274
|
+
else break;
|
|
273
275
|
}
|
|
276
|
+
programPath.node.body.splice(insertPos, 0, ...imports);
|
|
274
277
|
}
|
|
275
|
-
}
|
|
278
|
+
} }
|
|
276
279
|
};
|
|
277
280
|
};
|
|
278
281
|
|
|
279
282
|
//#endregion
|
|
280
|
-
exports.
|
|
281
|
-
//# sourceMappingURL=babel-plugin-intlayer.cjs.map
|
|
283
|
+
exports.intlayerOptimizeBabelPlugin = intlayerOptimizeBabelPlugin;
|
|
284
|
+
//# sourceMappingURL=babel-plugin-intlayer-optimize.cjs.map
|