react-native-boost 0.5.3 → 0.5.4
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/plugin/esm/index.mjs +174 -160
- package/dist/plugin/esm/index.mjs.map +1 -1
- package/dist/plugin/index.js +174 -160
- package/dist/plugin/index.js.map +1 -1
- package/package.json +1 -1
- package/src/plugin/optimizers/text/index.ts +127 -155
- package/src/plugin/optimizers/view/index.ts +13 -47
- package/src/plugin/utils/common/attributes.ts +144 -0
- package/src/plugin/utils/common/base.ts +82 -0
- package/src/plugin/utils/common/index.ts +4 -0
- package/src/plugin/utils/common/node-types.ts +22 -0
- package/src/plugin/utils/common/validation.ts +181 -0
- package/src/plugin/utils/constants.ts +16 -0
- package/src/plugin/utils/common.ts +0 -296
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
import { NodePath, types as t } from '@babel/core';
|
|
2
|
-
import { ensureArray } from './helpers';
|
|
3
|
-
import { FileImportOptions, HubFile } from '../types';
|
|
4
|
-
import { minimatch } from 'minimatch';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import PluginError from './plugin-error';
|
|
7
|
-
import { addDefault, addNamed } from '@babel/helper-module-imports';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Adds a hint to the file object to ensure that a specific import is added only once and cached on the file object.
|
|
11
|
-
*
|
|
12
|
-
* @param opts - Object containing the function arguments:
|
|
13
|
-
* - file: The Babel file object (e.g. HubFile)
|
|
14
|
-
* - nameHint: The name hint which also acts as the cache key to ensure the import is only added once (e.g. 'normalizeAccessibilityProps')
|
|
15
|
-
* - path: The current Babel NodePath
|
|
16
|
-
* - importName: The named import string (e.g. 'normalizeAccessibilityProps'), used when importType is 'named'
|
|
17
|
-
* - moduleName: The module to import from (e.g. 'react-native-boost')
|
|
18
|
-
* - importType: Either 'named' (default) or 'default' to determine the type of import to use.
|
|
19
|
-
*
|
|
20
|
-
* @returns The identifier returned by addNamed or addDefault.
|
|
21
|
-
*/
|
|
22
|
-
export function addFileImportHint({
|
|
23
|
-
file,
|
|
24
|
-
nameHint,
|
|
25
|
-
path,
|
|
26
|
-
importName,
|
|
27
|
-
moduleName,
|
|
28
|
-
importType = 'named',
|
|
29
|
-
}: FileImportOptions): t.Identifier {
|
|
30
|
-
if (!file.__hasImports?.[nameHint]) {
|
|
31
|
-
file.__hasImports = file.__hasImports || {};
|
|
32
|
-
file.__hasImports[nameHint] =
|
|
33
|
-
importType === 'default'
|
|
34
|
-
? addDefault(path, moduleName, { nameHint })
|
|
35
|
-
: addNamed(path, importName, moduleName, { nameHint });
|
|
36
|
-
}
|
|
37
|
-
return file.__hasImports[nameHint];
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Checks if the file is in the list of ignored files.
|
|
41
|
-
*
|
|
42
|
-
* @param p - The path to the JSXOpeningElement.
|
|
43
|
-
* @param ignores - List of glob paths (absolute or relative to import.meta.dirname).
|
|
44
|
-
* @returns true if the file matches any of the ignore patterns.
|
|
45
|
-
*/
|
|
46
|
-
export const isIgnoredFile = (p: NodePath<t.JSXOpeningElement>, ignores: string[]): boolean => {
|
|
47
|
-
const hub = p.hub as unknown;
|
|
48
|
-
const file = typeof hub === 'object' && hub !== null && 'file' in hub ? (hub.file as HubFile) : undefined;
|
|
49
|
-
|
|
50
|
-
if (!file) {
|
|
51
|
-
throw new PluginError('No file found in Babel hub');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const fileName = file.opts.filename;
|
|
55
|
-
|
|
56
|
-
// Use the current working directory which typically corresponds to the user's project root.
|
|
57
|
-
const baseDirectory = 'cwd' in file.opts ? (file.opts.cwd as string) : process.cwd();
|
|
58
|
-
|
|
59
|
-
// Iterate through the ignore patterns.
|
|
60
|
-
for (const pattern of ignores) {
|
|
61
|
-
// If the pattern is not absolute, join it with the baseDir
|
|
62
|
-
const absolutePattern = path.isAbsolute(pattern) ? pattern : path.join(baseDirectory, pattern);
|
|
63
|
-
|
|
64
|
-
// Check if the file name matches the glob pattern.
|
|
65
|
-
if (minimatch(fileName, absolutePattern, { dot: true })) {
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return false;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Checks if the JSX element should be ignored based on a preceding comment.
|
|
75
|
-
*
|
|
76
|
-
* The function looks up the JSXOpeningElement's own leading comments as well as
|
|
77
|
-
* the parent element's comments before falling back to inspect siblings.
|
|
78
|
-
*
|
|
79
|
-
* @param path - The path to the JSXOpeningElement.
|
|
80
|
-
* @returns true if the JSX element should be ignored.
|
|
81
|
-
*/
|
|
82
|
-
export const shouldIgnoreOptimization = (path: NodePath<t.JSXOpeningElement>): boolean => {
|
|
83
|
-
// Check for @boost-ignore in the leading comments on the JSX opening element.
|
|
84
|
-
if (path.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))) {
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Check for @boost-ignore in the leading comments on the parent JSX element.
|
|
89
|
-
const jsxElementPath = path.parentPath;
|
|
90
|
-
if (jsxElementPath.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))) {
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// NEW: Check for @boost-ignore in the leading comments on the ObjectProperty (if it exists)
|
|
95
|
-
// This handles cases where the JSX element is used as a value inside an object literal.
|
|
96
|
-
const propertyPath = jsxElementPath.parentPath;
|
|
97
|
-
if (
|
|
98
|
-
propertyPath &&
|
|
99
|
-
propertyPath.isObjectProperty() &&
|
|
100
|
-
propertyPath.node.leadingComments?.some((comment) => comment.value.includes('@boost-ignore'))
|
|
101
|
-
) {
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (!jsxElementPath.parentPath) return false;
|
|
106
|
-
|
|
107
|
-
// Get the container that holds this element (for example, a JSX fragment or JSX element)
|
|
108
|
-
const containerPath = jsxElementPath.parentPath;
|
|
109
|
-
const siblings = ensureArray(containerPath.get('children'));
|
|
110
|
-
const index = siblings.findIndex((sibling) => sibling.node === jsxElementPath.node);
|
|
111
|
-
if (index === -1) return false;
|
|
112
|
-
|
|
113
|
-
// Look backward from the current element for a non-empty node.
|
|
114
|
-
for (let index_ = index - 1; index_ >= 0; index_--) {
|
|
115
|
-
const sibling = siblings[index_];
|
|
116
|
-
// Skip over any whitespace (only in JSXText nodes)
|
|
117
|
-
if (sibling.isJSXText() && sibling.node.value.trim() === '') {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
// If the sibling is a JSX expression container, check its empty expression's comments.
|
|
121
|
-
if (sibling.isJSXExpressionContainer()) {
|
|
122
|
-
const expression = sibling.get('expression');
|
|
123
|
-
if (expression && expression.node) {
|
|
124
|
-
const comments = [
|
|
125
|
-
...(expression.node.leadingComments || []),
|
|
126
|
-
...(expression.node.trailingComments || []),
|
|
127
|
-
...(expression.node.innerComments || []),
|
|
128
|
-
].map((comment) => comment.value.trim());
|
|
129
|
-
if (comments.some((comment) => comment.includes('@boost-ignore'))) {
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// Also check if the node itself carries a leadingComments property.
|
|
135
|
-
if (
|
|
136
|
-
sibling.node.leadingComments &&
|
|
137
|
-
sibling.node.leadingComments.some((comment) => comment.value.includes('@boost-ignore'))
|
|
138
|
-
) {
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
break; // if the immediate non-whitespace node is not our ignore marker, stop
|
|
142
|
-
}
|
|
143
|
-
return false;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Checks if the JSX element has a blacklisted property.
|
|
148
|
-
*
|
|
149
|
-
* @param path - The path to the JSXOpeningElement.
|
|
150
|
-
* @param blacklist - The set of blacklisted properties.
|
|
151
|
-
* @returns true if the JSX element has a blacklisted property.
|
|
152
|
-
*/
|
|
153
|
-
export const hasBlacklistedProperty = (path: NodePath<t.JSXOpeningElement>, blacklist: Set<string>): boolean => {
|
|
154
|
-
return path.node.attributes.some((attribute) => {
|
|
155
|
-
// Check if we can resolve the spread attribute
|
|
156
|
-
if (t.isJSXSpreadAttribute(attribute)) {
|
|
157
|
-
if (t.isIdentifier(attribute.argument)) {
|
|
158
|
-
const binding = path.scope.getBinding(attribute.argument.name);
|
|
159
|
-
let objectExpression: t.ObjectExpression | undefined;
|
|
160
|
-
if (binding) {
|
|
161
|
-
// If the binding node is a VariableDeclarator, use its initializer
|
|
162
|
-
if (t.isVariableDeclarator(binding.path.node)) {
|
|
163
|
-
objectExpression = binding.path.node.init as t.ObjectExpression;
|
|
164
|
-
} else if (t.isObjectExpression(binding.path.node)) {
|
|
165
|
-
objectExpression = binding.path.node;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
if (objectExpression && t.isObjectExpression(objectExpression)) {
|
|
169
|
-
return objectExpression.properties.some((property) => {
|
|
170
|
-
if (t.isObjectProperty(property) && t.isIdentifier(property.key)) {
|
|
171
|
-
return blacklist.has(property.key.name);
|
|
172
|
-
}
|
|
173
|
-
return false;
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
// Bail if we can't resolve the spread attribute
|
|
178
|
-
return true;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// For other attribute types (e.g. namespaced), assume no blacklisting
|
|
182
|
-
return false;
|
|
183
|
-
});
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Helper that builds an Object.assign expression out of the existing JSX attributes.
|
|
188
|
-
* It handles both plain JSXAttributes and spread attributes.
|
|
189
|
-
*
|
|
190
|
-
* @param attributes - The attributes to build the expression from.
|
|
191
|
-
* @returns The Object.assign expression.
|
|
192
|
-
*/
|
|
193
|
-
export const buildPropertiesFromAttributes = (attributes: (t.JSXAttribute | t.JSXSpreadAttribute)[]): t.Expression => {
|
|
194
|
-
const arguments_: t.Expression[] = [];
|
|
195
|
-
for (const attribute of attributes) {
|
|
196
|
-
if (t.isJSXSpreadAttribute(attribute)) {
|
|
197
|
-
arguments_.push(attribute.argument);
|
|
198
|
-
} else if (t.isJSXAttribute(attribute)) {
|
|
199
|
-
const key = attribute.name.name;
|
|
200
|
-
let value: t.Expression;
|
|
201
|
-
if (!attribute.value) {
|
|
202
|
-
value = t.booleanLiteral(true);
|
|
203
|
-
} else if (t.isStringLiteral(attribute.value)) {
|
|
204
|
-
value = attribute.value;
|
|
205
|
-
} else if (t.isJSXExpressionContainer(attribute.value)) {
|
|
206
|
-
value = t.isJSXEmptyExpression(attribute.value.expression)
|
|
207
|
-
? t.booleanLiteral(true)
|
|
208
|
-
: attribute.value.expression;
|
|
209
|
-
} else {
|
|
210
|
-
value = t.nullLiteral();
|
|
211
|
-
}
|
|
212
|
-
// If the key is not a valid JavaScript identifier (e.g. "aria-label"), use a string literal.
|
|
213
|
-
const validIdentifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
214
|
-
const keyNode =
|
|
215
|
-
typeof key === 'string' && validIdentifierRegex.test(key) ? t.identifier(key) : t.stringLiteral(key.toString());
|
|
216
|
-
|
|
217
|
-
arguments_.push(t.objectExpression([t.objectProperty(keyNode, value)]));
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
if (arguments_.length === 0) {
|
|
221
|
-
return t.objectExpression([]);
|
|
222
|
-
}
|
|
223
|
-
return t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('assign')), [
|
|
224
|
-
t.objectExpression([]),
|
|
225
|
-
...arguments_,
|
|
226
|
-
]);
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* The set of accessibility properties that need to be normalized.
|
|
231
|
-
*/
|
|
232
|
-
export const accessibilityProperties = new Set([
|
|
233
|
-
'accessibilityLabel',
|
|
234
|
-
'aria-label',
|
|
235
|
-
'accessibilityState',
|
|
236
|
-
'aria-busy',
|
|
237
|
-
'aria-checked',
|
|
238
|
-
'aria-disabled',
|
|
239
|
-
'aria-expanded',
|
|
240
|
-
'aria-selected',
|
|
241
|
-
'accessible',
|
|
242
|
-
]);
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Checks if the JSX element has an accessibility property.
|
|
246
|
-
*
|
|
247
|
-
* @param path - The NodePath for the JSXOpeningElement, used for scope lookup.
|
|
248
|
-
* @param attributes - The attributes to check.
|
|
249
|
-
* @returns true if the JSX element has an accessibility property.
|
|
250
|
-
*/
|
|
251
|
-
export const hasAccessibilityProperty = (
|
|
252
|
-
path: NodePath<t.JSXOpeningElement>,
|
|
253
|
-
attributes: (t.JSXAttribute | t.JSXSpreadAttribute)[]
|
|
254
|
-
): boolean => {
|
|
255
|
-
for (const attribute of attributes) {
|
|
256
|
-
if (t.isJSXAttribute(attribute)) {
|
|
257
|
-
const key = attribute.name.name;
|
|
258
|
-
if (typeof key === 'string' && accessibilityProperties.has(key)) {
|
|
259
|
-
return true;
|
|
260
|
-
}
|
|
261
|
-
} else if (t.isJSXSpreadAttribute(attribute)) {
|
|
262
|
-
if (t.isObjectExpression(attribute.argument)) {
|
|
263
|
-
for (const property of attribute.argument.properties) {
|
|
264
|
-
if (
|
|
265
|
-
t.isObjectProperty(property) &&
|
|
266
|
-
t.isIdentifier(property.key) &&
|
|
267
|
-
accessibilityProperties.has(property.key.name)
|
|
268
|
-
) {
|
|
269
|
-
return true;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
} else if (t.isIdentifier(attribute.argument)) {
|
|
273
|
-
const binding = path.scope.getBinding(attribute.argument.name);
|
|
274
|
-
if (binding && t.isVariableDeclarator(binding.path.node)) {
|
|
275
|
-
const declarator = binding.path.node as t.VariableDeclarator;
|
|
276
|
-
if (declarator.init && t.isObjectExpression(declarator.init)) {
|
|
277
|
-
for (const property of declarator.init.properties) {
|
|
278
|
-
if (
|
|
279
|
-
t.isObjectProperty(property) &&
|
|
280
|
-
t.isIdentifier(property.key) &&
|
|
281
|
-
accessibilityProperties.has(property.key.name)
|
|
282
|
-
) {
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
continue;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
return true;
|
|
290
|
-
} else {
|
|
291
|
-
return true;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return false;
|
|
296
|
-
};
|