@lwc/babel-plugin-component 9.0.3 → 9.0.4-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +3 -1
- package/dist/index.cjs +220 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +219 -2
- package/dist/private-method-transform.d.ts +18 -0
- package/dist/reverse-private-method-transform.d.ts +18 -0
- package/dist/utils.d.ts +8 -1
- package/package.json +3 -3
package/dist/constants.d.ts
CHANGED
|
@@ -23,7 +23,9 @@ declare const TEMPLATE_KEY = "tmpl";
|
|
|
23
23
|
declare const COMPONENT_NAME_KEY = "sel";
|
|
24
24
|
declare const API_VERSION_KEY = "apiVersion";
|
|
25
25
|
declare const COMPONENT_CLASS_ID = "__lwc_component_class_internal";
|
|
26
|
+
declare const PRIVATE_METHOD_PREFIX = "__lwc_component_class_internal_private_";
|
|
27
|
+
declare const PRIVATE_METHOD_METADATA_KEY = "__lwcTransformedPrivateMethods";
|
|
26
28
|
declare const SYNTHETIC_ELEMENT_INTERNALS_KEY = "enableSyntheticElementInternals";
|
|
27
29
|
declare const COMPONENT_FEATURE_FLAG_KEY = "componentFeatureFlag";
|
|
28
|
-
export { DECORATOR_TYPES, LWC_PACKAGE_ALIAS, LWC_PACKAGE_EXPORTS, LWC_COMPONENT_PROPERTIES, REGISTER_COMPONENT_ID, REGISTER_DECORATORS_ID, TEMPLATE_KEY, COMPONENT_NAME_KEY, API_VERSION_KEY, COMPONENT_CLASS_ID, SYNTHETIC_ELEMENT_INTERNALS_KEY, COMPONENT_FEATURE_FLAG_KEY, };
|
|
30
|
+
export { DECORATOR_TYPES, LWC_PACKAGE_ALIAS, LWC_PACKAGE_EXPORTS, LWC_COMPONENT_PROPERTIES, REGISTER_COMPONENT_ID, REGISTER_DECORATORS_ID, TEMPLATE_KEY, COMPONENT_NAME_KEY, API_VERSION_KEY, COMPONENT_CLASS_ID, PRIVATE_METHOD_PREFIX, PRIVATE_METHOD_METADATA_KEY, SYNTHETIC_ELEMENT_INTERNALS_KEY, COMPONENT_FEATURE_FLAG_KEY, };
|
|
29
31
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/index.cjs
CHANGED
|
@@ -41,6 +41,8 @@ const TEMPLATE_KEY = 'tmpl';
|
|
|
41
41
|
const COMPONENT_NAME_KEY = 'sel';
|
|
42
42
|
const API_VERSION_KEY = 'apiVersion';
|
|
43
43
|
const COMPONENT_CLASS_ID = '__lwc_component_class_internal';
|
|
44
|
+
const PRIVATE_METHOD_PREFIX = '__lwc_component_class_internal_private_';
|
|
45
|
+
const PRIVATE_METHOD_METADATA_KEY = '__lwcTransformedPrivateMethods';
|
|
44
46
|
const SYNTHETIC_ELEMENT_INTERNALS_KEY = 'enableSyntheticElementInternals';
|
|
45
47
|
const COMPONENT_FEATURE_FLAG_KEY = 'componentFeatureFlag';
|
|
46
48
|
|
|
@@ -259,6 +261,30 @@ function incrementMetricCounter(metric, state) {
|
|
|
259
261
|
function isErrorRecoveryMode(state) {
|
|
260
262
|
return state.file.opts?.parserOpts?.errorRecovery ?? false;
|
|
261
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Copies optional metadata properties between ClassMethod and ClassPrivateMethod nodes.
|
|
266
|
+
* These properties are not accepted by the t.classMethod() / t.classPrivateMethod() builders,
|
|
267
|
+
* so they must be transferred manually after node creation. Both the forward and reverse
|
|
268
|
+
* private-method transforms use this to maintain round-trip parity.
|
|
269
|
+
*/
|
|
270
|
+
function copyMethodMetadata(source, target) {
|
|
271
|
+
if (source.returnType != null)
|
|
272
|
+
target.returnType = source.returnType;
|
|
273
|
+
if (source.typeParameters != null)
|
|
274
|
+
target.typeParameters = source.typeParameters;
|
|
275
|
+
if (source.loc != null)
|
|
276
|
+
target.loc = source.loc;
|
|
277
|
+
if (source.abstract != null)
|
|
278
|
+
target.abstract = source.abstract;
|
|
279
|
+
if (source.access != null)
|
|
280
|
+
target.access = source.access;
|
|
281
|
+
if (source.accessibility != null)
|
|
282
|
+
target.accessibility = source.accessibility;
|
|
283
|
+
if (source.optional != null)
|
|
284
|
+
target.optional = source.optional;
|
|
285
|
+
if (source.override != null)
|
|
286
|
+
target.override = source.override;
|
|
287
|
+
}
|
|
262
288
|
|
|
263
289
|
/*
|
|
264
290
|
* Copyright (c) 2023, salesforce.com, inc.
|
|
@@ -1244,6 +1270,197 @@ function compilerVersionNumber({ types: t }) {
|
|
|
1244
1270
|
};
|
|
1245
1271
|
}
|
|
1246
1272
|
|
|
1273
|
+
/*
|
|
1274
|
+
* Copyright (c) 2025, salesforce.com, inc.
|
|
1275
|
+
* All rights reserved.
|
|
1276
|
+
* SPDX-License-Identifier: MIT
|
|
1277
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
1278
|
+
*/
|
|
1279
|
+
// We only transform kind: 'method'. Other kinds ('get', 'set', 'constructor') are left alone.
|
|
1280
|
+
const METHOD_KIND = 'method';
|
|
1281
|
+
/**
|
|
1282
|
+
* Standalone Babel plugin that transforms private method identifiers from
|
|
1283
|
+
* `#privateMethod` to `__lwc_component_class_internal_private_privateMethod`.
|
|
1284
|
+
*
|
|
1285
|
+
* This must be registered BEFORE the main LWC class transform plugin so that
|
|
1286
|
+
* private methods are converted to regular methods before decorator and class
|
|
1287
|
+
* property processing.
|
|
1288
|
+
*
|
|
1289
|
+
* Uses Program > path.traverse() rather than a top-level ClassPrivateMethod visitor
|
|
1290
|
+
* because the reverse transform has a ClassMethod visitor in the same Babel pass.
|
|
1291
|
+
* A direct ClassPrivateMethod visitor would replace nodes that the reverse transform
|
|
1292
|
+
* immediately converts back, creating an infinite loop. The manual traverse ensures
|
|
1293
|
+
* all forward replacements complete before the reverse visitor sees any ClassMethod.
|
|
1294
|
+
*/
|
|
1295
|
+
function privateMethodTransform({ types: t, }) {
|
|
1296
|
+
return {
|
|
1297
|
+
visitor: {
|
|
1298
|
+
Program(path, state) {
|
|
1299
|
+
const transformedNames = new Set();
|
|
1300
|
+
// Phase 1: Collect base names of all private methods (kind: 'method')
|
|
1301
|
+
// so that Phase 2 can transform invocations even for forward references
|
|
1302
|
+
// (call site visited before the method definition).
|
|
1303
|
+
const privateMethodBaseNames = new Set();
|
|
1304
|
+
path.traverse({
|
|
1305
|
+
ClassPrivateMethod(methodPath) {
|
|
1306
|
+
const key = methodPath.get('key');
|
|
1307
|
+
if (key.isPrivateName() && methodPath.node.kind === METHOD_KIND) {
|
|
1308
|
+
privateMethodBaseNames.add(key.node.id.name);
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
});
|
|
1312
|
+
// Phase 2: Transform definitions and invocations
|
|
1313
|
+
path.traverse({
|
|
1314
|
+
ClassPrivateMethod(methodPath, methodState) {
|
|
1315
|
+
const key = methodPath.get('key');
|
|
1316
|
+
if (!key.isPrivateName()) {
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (methodPath.node.kind !== METHOD_KIND) {
|
|
1320
|
+
handleError(methodPath, {
|
|
1321
|
+
errorInfo: errors.DecoratorErrors.UNSUPPORTED_PRIVATE_MEMBER,
|
|
1322
|
+
messageArgs: ['accessor methods'],
|
|
1323
|
+
}, methodState);
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
const node = methodPath.node;
|
|
1327
|
+
// Reject private methods with decorators (e.g. @api, @track, @wire)
|
|
1328
|
+
if (node.decorators && node.decorators.length > 0) {
|
|
1329
|
+
handleError(methodPath, {
|
|
1330
|
+
errorInfo: errors.DecoratorErrors.DECORATOR_ON_PRIVATE_METHOD,
|
|
1331
|
+
}, methodState);
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
const privateName = key.node.id.name;
|
|
1335
|
+
const transformedName = `${PRIVATE_METHOD_PREFIX}${privateName}`;
|
|
1336
|
+
const keyReplacement = t.identifier(transformedName);
|
|
1337
|
+
// Create a new ClassMethod node to replace the ClassPrivateMethod
|
|
1338
|
+
// https://babeljs.io/docs/babel-types#classmethod
|
|
1339
|
+
const classMethod = t.classMethod(METHOD_KIND, keyReplacement, node.params, node.body, node.computed, node.static, node.generator, node.async);
|
|
1340
|
+
copyMethodMetadata(node, classMethod);
|
|
1341
|
+
// Replace the entire ClassPrivateMethod node with the new ClassMethod node
|
|
1342
|
+
// (we can't just replace the key of type PrivateName with type Identifier)
|
|
1343
|
+
methodPath.replaceWith(classMethod);
|
|
1344
|
+
transformedNames.add(transformedName);
|
|
1345
|
+
},
|
|
1346
|
+
MemberExpression(memberPath) {
|
|
1347
|
+
const property = memberPath.node.property;
|
|
1348
|
+
if (t.isPrivateName(property)) {
|
|
1349
|
+
const baseName = property.id.name;
|
|
1350
|
+
if (privateMethodBaseNames.has(baseName)) {
|
|
1351
|
+
const prefixedName = `${PRIVATE_METHOD_PREFIX}${baseName}`;
|
|
1352
|
+
memberPath
|
|
1353
|
+
.get('property')
|
|
1354
|
+
.replaceWith(t.identifier(prefixedName));
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
},
|
|
1358
|
+
ClassPrivateProperty(propPath, propState) {
|
|
1359
|
+
handleError(propPath, {
|
|
1360
|
+
errorInfo: errors.DecoratorErrors.UNSUPPORTED_PRIVATE_MEMBER,
|
|
1361
|
+
messageArgs: ['fields'],
|
|
1362
|
+
}, propState);
|
|
1363
|
+
},
|
|
1364
|
+
}, state);
|
|
1365
|
+
state.file.metadata[PRIVATE_METHOD_METADATA_KEY] = transformedNames;
|
|
1366
|
+
},
|
|
1367
|
+
},
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
/*
|
|
1372
|
+
* Copyright (c) 2025, salesforce.com, inc.
|
|
1373
|
+
* All rights reserved.
|
|
1374
|
+
* SPDX-License-Identifier: MIT
|
|
1375
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
1376
|
+
*/
|
|
1377
|
+
/**
|
|
1378
|
+
* Standalone Babel plugin that reverses the private method transformation by converting
|
|
1379
|
+
* methods with prefix {@link PRIVATE_METHOD_PREFIX} back to ClassPrivateMethod nodes,
|
|
1380
|
+
* and restoring prefixed MemberExpression properties back to PrivateName nodes.
|
|
1381
|
+
*
|
|
1382
|
+
* This must be registered AFTER @babel/plugin-transform-class-properties so that
|
|
1383
|
+
* class properties are fully transformed before private methods are restored.
|
|
1384
|
+
*
|
|
1385
|
+
* Round-trip parity: to match {@link ./private-method-transform.ts}, this transform must copy the same
|
|
1386
|
+
* properties from ClassMethod onto ClassPrivateMethod when present: returnType, typeParameters, loc,
|
|
1387
|
+
* abstract, access, accessibility, optional, override (plus async, generator, computed from the builder).
|
|
1388
|
+
*
|
|
1389
|
+
* @see {@link ./private-method-transform.ts} for original transformation
|
|
1390
|
+
*/
|
|
1391
|
+
function reversePrivateMethodTransform({ types: t, }) {
|
|
1392
|
+
// Scoped to this plugin instance's closure. Safe as long as each Babel run creates a
|
|
1393
|
+
// fresh plugin via LwcReversePrivateMethodTransform(); would accumulate across files if
|
|
1394
|
+
// the same instance were ever reused.
|
|
1395
|
+
const reverseTransformedNames = new Set();
|
|
1396
|
+
return {
|
|
1397
|
+
visitor: {
|
|
1398
|
+
ClassMethod(path, state) {
|
|
1399
|
+
const key = path.get('key');
|
|
1400
|
+
// kind: 'method' | 'get' | 'set' - only 'method' is in scope.
|
|
1401
|
+
if (key.isIdentifier() && path.node.kind === 'method') {
|
|
1402
|
+
const methodName = key.node.name;
|
|
1403
|
+
if (methodName.startsWith(PRIVATE_METHOD_PREFIX)) {
|
|
1404
|
+
const forwardTransformedNames = state.file.metadata[PRIVATE_METHOD_METADATA_KEY];
|
|
1405
|
+
// If the method was not transformed by the forward pass, it is a
|
|
1406
|
+
// user-defined method that collides with the reserved prefix.
|
|
1407
|
+
if (!forwardTransformedNames || !forwardTransformedNames.has(methodName)) {
|
|
1408
|
+
const message = errors.DecoratorErrors.PRIVATE_METHOD_NAME_COLLISION.message.replace('{0}', methodName);
|
|
1409
|
+
throw path.buildCodeFrameError(message);
|
|
1410
|
+
}
|
|
1411
|
+
const originalPrivateName = methodName.replace(PRIVATE_METHOD_PREFIX, '');
|
|
1412
|
+
const node = path.node;
|
|
1413
|
+
const classPrivateMethod = t.classPrivateMethod('method', t.privateName(t.identifier(originalPrivateName)), node.params, node.body, node.static);
|
|
1414
|
+
// Properties the t.classPrivateMethod() builder doesn't accept
|
|
1415
|
+
classPrivateMethod.async = node.async;
|
|
1416
|
+
classPrivateMethod.generator = node.generator;
|
|
1417
|
+
classPrivateMethod.computed = node.computed;
|
|
1418
|
+
copyMethodMetadata(node, classPrivateMethod);
|
|
1419
|
+
path.replaceWith(classPrivateMethod);
|
|
1420
|
+
reverseTransformedNames.add(methodName);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
},
|
|
1424
|
+
MemberExpression(path, state) {
|
|
1425
|
+
const property = path.node.property;
|
|
1426
|
+
if (!t.isIdentifier(property) || !property.name.startsWith(PRIVATE_METHOD_PREFIX)) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
const forwardTransformedNames = state.file.metadata[PRIVATE_METHOD_METADATA_KEY];
|
|
1430
|
+
if (!forwardTransformedNames || !forwardTransformedNames.has(property.name)) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
const originalName = property.name.replace(PRIVATE_METHOD_PREFIX, '');
|
|
1434
|
+
path.get('property').replaceWith(t.privateName(t.identifier(originalName)));
|
|
1435
|
+
},
|
|
1436
|
+
// After all nodes have been visited, verify that every method the forward transform
|
|
1437
|
+
// renamed was also restored by the reverse transform. A mismatch here means an
|
|
1438
|
+
// intermediate plugin (e.g. @babel/plugin-transform-class-properties) removed or
|
|
1439
|
+
// renamed a prefixed method, leaving a mangled name in the final output.
|
|
1440
|
+
Program: {
|
|
1441
|
+
exit(_path, state) {
|
|
1442
|
+
const forwardTransformedNames = state.file.metadata[PRIVATE_METHOD_METADATA_KEY];
|
|
1443
|
+
if (!forwardTransformedNames) {
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
const missingFromReverse = [];
|
|
1447
|
+
for (const name of forwardTransformedNames) {
|
|
1448
|
+
if (!reverseTransformedNames.has(name)) {
|
|
1449
|
+
missingFromReverse.push(name);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
if (missingFromReverse.length > 0) {
|
|
1453
|
+
throw new Error(`Private method transform count mismatch: ` +
|
|
1454
|
+
`forward transformed ${forwardTransformedNames.size} method(s), ` +
|
|
1455
|
+
`but reverse transformed ${reverseTransformedNames.size}. ` +
|
|
1456
|
+
`Missing reverse transforms for: ${missingFromReverse.join(', ')}`);
|
|
1457
|
+
}
|
|
1458
|
+
},
|
|
1459
|
+
},
|
|
1460
|
+
},
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1247
1464
|
/*
|
|
1248
1465
|
* Copyright (c) 2023, salesforce.com, inc.
|
|
1249
1466
|
* All rights reserved.
|
|
@@ -1292,6 +1509,8 @@ function LwcClassTransform(api) {
|
|
|
1292
1509
|
};
|
|
1293
1510
|
}
|
|
1294
1511
|
|
|
1512
|
+
exports.LwcPrivateMethodTransform = privateMethodTransform;
|
|
1513
|
+
exports.LwcReversePrivateMethodTransform = reversePrivateMethodTransform;
|
|
1295
1514
|
exports.default = LwcClassTransform;
|
|
1296
|
-
/** version: 9.0.
|
|
1515
|
+
/** version: 9.0.4-alpha.0 */
|
|
1297
1516
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { BabelAPI, LwcBabelPluginPass } from './types';
|
|
2
2
|
import type { PluginObj } from '@babel/core';
|
|
3
3
|
export type { LwcBabelPluginOptions } from './types';
|
|
4
|
+
export { default as LwcPrivateMethodTransform } from './private-method-transform';
|
|
5
|
+
export { default as LwcReversePrivateMethodTransform } from './reverse-private-method-transform';
|
|
4
6
|
/**
|
|
5
7
|
* The transform is done in 2 passes:
|
|
6
8
|
* - First, apply in a single AST traversal the decorators and the component transformation.
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,8 @@ const TEMPLATE_KEY = 'tmpl';
|
|
|
37
37
|
const COMPONENT_NAME_KEY = 'sel';
|
|
38
38
|
const API_VERSION_KEY = 'apiVersion';
|
|
39
39
|
const COMPONENT_CLASS_ID = '__lwc_component_class_internal';
|
|
40
|
+
const PRIVATE_METHOD_PREFIX = '__lwc_component_class_internal_private_';
|
|
41
|
+
const PRIVATE_METHOD_METADATA_KEY = '__lwcTransformedPrivateMethods';
|
|
40
42
|
const SYNTHETIC_ELEMENT_INTERNALS_KEY = 'enableSyntheticElementInternals';
|
|
41
43
|
const COMPONENT_FEATURE_FLAG_KEY = 'componentFeatureFlag';
|
|
42
44
|
|
|
@@ -255,6 +257,30 @@ function incrementMetricCounter(metric, state) {
|
|
|
255
257
|
function isErrorRecoveryMode(state) {
|
|
256
258
|
return state.file.opts?.parserOpts?.errorRecovery ?? false;
|
|
257
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Copies optional metadata properties between ClassMethod and ClassPrivateMethod nodes.
|
|
262
|
+
* These properties are not accepted by the t.classMethod() / t.classPrivateMethod() builders,
|
|
263
|
+
* so they must be transferred manually after node creation. Both the forward and reverse
|
|
264
|
+
* private-method transforms use this to maintain round-trip parity.
|
|
265
|
+
*/
|
|
266
|
+
function copyMethodMetadata(source, target) {
|
|
267
|
+
if (source.returnType != null)
|
|
268
|
+
target.returnType = source.returnType;
|
|
269
|
+
if (source.typeParameters != null)
|
|
270
|
+
target.typeParameters = source.typeParameters;
|
|
271
|
+
if (source.loc != null)
|
|
272
|
+
target.loc = source.loc;
|
|
273
|
+
if (source.abstract != null)
|
|
274
|
+
target.abstract = source.abstract;
|
|
275
|
+
if (source.access != null)
|
|
276
|
+
target.access = source.access;
|
|
277
|
+
if (source.accessibility != null)
|
|
278
|
+
target.accessibility = source.accessibility;
|
|
279
|
+
if (source.optional != null)
|
|
280
|
+
target.optional = source.optional;
|
|
281
|
+
if (source.override != null)
|
|
282
|
+
target.override = source.override;
|
|
283
|
+
}
|
|
258
284
|
|
|
259
285
|
/*
|
|
260
286
|
* Copyright (c) 2023, salesforce.com, inc.
|
|
@@ -1240,6 +1266,197 @@ function compilerVersionNumber({ types: t }) {
|
|
|
1240
1266
|
};
|
|
1241
1267
|
}
|
|
1242
1268
|
|
|
1269
|
+
/*
|
|
1270
|
+
* Copyright (c) 2025, salesforce.com, inc.
|
|
1271
|
+
* All rights reserved.
|
|
1272
|
+
* SPDX-License-Identifier: MIT
|
|
1273
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
1274
|
+
*/
|
|
1275
|
+
// We only transform kind: 'method'. Other kinds ('get', 'set', 'constructor') are left alone.
|
|
1276
|
+
const METHOD_KIND = 'method';
|
|
1277
|
+
/**
|
|
1278
|
+
* Standalone Babel plugin that transforms private method identifiers from
|
|
1279
|
+
* `#privateMethod` to `__lwc_component_class_internal_private_privateMethod`.
|
|
1280
|
+
*
|
|
1281
|
+
* This must be registered BEFORE the main LWC class transform plugin so that
|
|
1282
|
+
* private methods are converted to regular methods before decorator and class
|
|
1283
|
+
* property processing.
|
|
1284
|
+
*
|
|
1285
|
+
* Uses Program > path.traverse() rather than a top-level ClassPrivateMethod visitor
|
|
1286
|
+
* because the reverse transform has a ClassMethod visitor in the same Babel pass.
|
|
1287
|
+
* A direct ClassPrivateMethod visitor would replace nodes that the reverse transform
|
|
1288
|
+
* immediately converts back, creating an infinite loop. The manual traverse ensures
|
|
1289
|
+
* all forward replacements complete before the reverse visitor sees any ClassMethod.
|
|
1290
|
+
*/
|
|
1291
|
+
function privateMethodTransform({ types: t, }) {
|
|
1292
|
+
return {
|
|
1293
|
+
visitor: {
|
|
1294
|
+
Program(path, state) {
|
|
1295
|
+
const transformedNames = new Set();
|
|
1296
|
+
// Phase 1: Collect base names of all private methods (kind: 'method')
|
|
1297
|
+
// so that Phase 2 can transform invocations even for forward references
|
|
1298
|
+
// (call site visited before the method definition).
|
|
1299
|
+
const privateMethodBaseNames = new Set();
|
|
1300
|
+
path.traverse({
|
|
1301
|
+
ClassPrivateMethod(methodPath) {
|
|
1302
|
+
const key = methodPath.get('key');
|
|
1303
|
+
if (key.isPrivateName() && methodPath.node.kind === METHOD_KIND) {
|
|
1304
|
+
privateMethodBaseNames.add(key.node.id.name);
|
|
1305
|
+
}
|
|
1306
|
+
},
|
|
1307
|
+
});
|
|
1308
|
+
// Phase 2: Transform definitions and invocations
|
|
1309
|
+
path.traverse({
|
|
1310
|
+
ClassPrivateMethod(methodPath, methodState) {
|
|
1311
|
+
const key = methodPath.get('key');
|
|
1312
|
+
if (!key.isPrivateName()) {
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
if (methodPath.node.kind !== METHOD_KIND) {
|
|
1316
|
+
handleError(methodPath, {
|
|
1317
|
+
errorInfo: DecoratorErrors.UNSUPPORTED_PRIVATE_MEMBER,
|
|
1318
|
+
messageArgs: ['accessor methods'],
|
|
1319
|
+
}, methodState);
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
const node = methodPath.node;
|
|
1323
|
+
// Reject private methods with decorators (e.g. @api, @track, @wire)
|
|
1324
|
+
if (node.decorators && node.decorators.length > 0) {
|
|
1325
|
+
handleError(methodPath, {
|
|
1326
|
+
errorInfo: DecoratorErrors.DECORATOR_ON_PRIVATE_METHOD,
|
|
1327
|
+
}, methodState);
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
const privateName = key.node.id.name;
|
|
1331
|
+
const transformedName = `${PRIVATE_METHOD_PREFIX}${privateName}`;
|
|
1332
|
+
const keyReplacement = t.identifier(transformedName);
|
|
1333
|
+
// Create a new ClassMethod node to replace the ClassPrivateMethod
|
|
1334
|
+
// https://babeljs.io/docs/babel-types#classmethod
|
|
1335
|
+
const classMethod = t.classMethod(METHOD_KIND, keyReplacement, node.params, node.body, node.computed, node.static, node.generator, node.async);
|
|
1336
|
+
copyMethodMetadata(node, classMethod);
|
|
1337
|
+
// Replace the entire ClassPrivateMethod node with the new ClassMethod node
|
|
1338
|
+
// (we can't just replace the key of type PrivateName with type Identifier)
|
|
1339
|
+
methodPath.replaceWith(classMethod);
|
|
1340
|
+
transformedNames.add(transformedName);
|
|
1341
|
+
},
|
|
1342
|
+
MemberExpression(memberPath) {
|
|
1343
|
+
const property = memberPath.node.property;
|
|
1344
|
+
if (t.isPrivateName(property)) {
|
|
1345
|
+
const baseName = property.id.name;
|
|
1346
|
+
if (privateMethodBaseNames.has(baseName)) {
|
|
1347
|
+
const prefixedName = `${PRIVATE_METHOD_PREFIX}${baseName}`;
|
|
1348
|
+
memberPath
|
|
1349
|
+
.get('property')
|
|
1350
|
+
.replaceWith(t.identifier(prefixedName));
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
},
|
|
1354
|
+
ClassPrivateProperty(propPath, propState) {
|
|
1355
|
+
handleError(propPath, {
|
|
1356
|
+
errorInfo: DecoratorErrors.UNSUPPORTED_PRIVATE_MEMBER,
|
|
1357
|
+
messageArgs: ['fields'],
|
|
1358
|
+
}, propState);
|
|
1359
|
+
},
|
|
1360
|
+
}, state);
|
|
1361
|
+
state.file.metadata[PRIVATE_METHOD_METADATA_KEY] = transformedNames;
|
|
1362
|
+
},
|
|
1363
|
+
},
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/*
|
|
1368
|
+
* Copyright (c) 2025, salesforce.com, inc.
|
|
1369
|
+
* All rights reserved.
|
|
1370
|
+
* SPDX-License-Identifier: MIT
|
|
1371
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
1372
|
+
*/
|
|
1373
|
+
/**
|
|
1374
|
+
* Standalone Babel plugin that reverses the private method transformation by converting
|
|
1375
|
+
* methods with prefix {@link PRIVATE_METHOD_PREFIX} back to ClassPrivateMethod nodes,
|
|
1376
|
+
* and restoring prefixed MemberExpression properties back to PrivateName nodes.
|
|
1377
|
+
*
|
|
1378
|
+
* This must be registered AFTER @babel/plugin-transform-class-properties so that
|
|
1379
|
+
* class properties are fully transformed before private methods are restored.
|
|
1380
|
+
*
|
|
1381
|
+
* Round-trip parity: to match {@link ./private-method-transform.ts}, this transform must copy the same
|
|
1382
|
+
* properties from ClassMethod onto ClassPrivateMethod when present: returnType, typeParameters, loc,
|
|
1383
|
+
* abstract, access, accessibility, optional, override (plus async, generator, computed from the builder).
|
|
1384
|
+
*
|
|
1385
|
+
* @see {@link ./private-method-transform.ts} for original transformation
|
|
1386
|
+
*/
|
|
1387
|
+
function reversePrivateMethodTransform({ types: t, }) {
|
|
1388
|
+
// Scoped to this plugin instance's closure. Safe as long as each Babel run creates a
|
|
1389
|
+
// fresh plugin via LwcReversePrivateMethodTransform(); would accumulate across files if
|
|
1390
|
+
// the same instance were ever reused.
|
|
1391
|
+
const reverseTransformedNames = new Set();
|
|
1392
|
+
return {
|
|
1393
|
+
visitor: {
|
|
1394
|
+
ClassMethod(path, state) {
|
|
1395
|
+
const key = path.get('key');
|
|
1396
|
+
// kind: 'method' | 'get' | 'set' - only 'method' is in scope.
|
|
1397
|
+
if (key.isIdentifier() && path.node.kind === 'method') {
|
|
1398
|
+
const methodName = key.node.name;
|
|
1399
|
+
if (methodName.startsWith(PRIVATE_METHOD_PREFIX)) {
|
|
1400
|
+
const forwardTransformedNames = state.file.metadata[PRIVATE_METHOD_METADATA_KEY];
|
|
1401
|
+
// If the method was not transformed by the forward pass, it is a
|
|
1402
|
+
// user-defined method that collides with the reserved prefix.
|
|
1403
|
+
if (!forwardTransformedNames || !forwardTransformedNames.has(methodName)) {
|
|
1404
|
+
const message = DecoratorErrors.PRIVATE_METHOD_NAME_COLLISION.message.replace('{0}', methodName);
|
|
1405
|
+
throw path.buildCodeFrameError(message);
|
|
1406
|
+
}
|
|
1407
|
+
const originalPrivateName = methodName.replace(PRIVATE_METHOD_PREFIX, '');
|
|
1408
|
+
const node = path.node;
|
|
1409
|
+
const classPrivateMethod = t.classPrivateMethod('method', t.privateName(t.identifier(originalPrivateName)), node.params, node.body, node.static);
|
|
1410
|
+
// Properties the t.classPrivateMethod() builder doesn't accept
|
|
1411
|
+
classPrivateMethod.async = node.async;
|
|
1412
|
+
classPrivateMethod.generator = node.generator;
|
|
1413
|
+
classPrivateMethod.computed = node.computed;
|
|
1414
|
+
copyMethodMetadata(node, classPrivateMethod);
|
|
1415
|
+
path.replaceWith(classPrivateMethod);
|
|
1416
|
+
reverseTransformedNames.add(methodName);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
},
|
|
1420
|
+
MemberExpression(path, state) {
|
|
1421
|
+
const property = path.node.property;
|
|
1422
|
+
if (!t.isIdentifier(property) || !property.name.startsWith(PRIVATE_METHOD_PREFIX)) {
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
const forwardTransformedNames = state.file.metadata[PRIVATE_METHOD_METADATA_KEY];
|
|
1426
|
+
if (!forwardTransformedNames || !forwardTransformedNames.has(property.name)) {
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
const originalName = property.name.replace(PRIVATE_METHOD_PREFIX, '');
|
|
1430
|
+
path.get('property').replaceWith(t.privateName(t.identifier(originalName)));
|
|
1431
|
+
},
|
|
1432
|
+
// After all nodes have been visited, verify that every method the forward transform
|
|
1433
|
+
// renamed was also restored by the reverse transform. A mismatch here means an
|
|
1434
|
+
// intermediate plugin (e.g. @babel/plugin-transform-class-properties) removed or
|
|
1435
|
+
// renamed a prefixed method, leaving a mangled name in the final output.
|
|
1436
|
+
Program: {
|
|
1437
|
+
exit(_path, state) {
|
|
1438
|
+
const forwardTransformedNames = state.file.metadata[PRIVATE_METHOD_METADATA_KEY];
|
|
1439
|
+
if (!forwardTransformedNames) {
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
const missingFromReverse = [];
|
|
1443
|
+
for (const name of forwardTransformedNames) {
|
|
1444
|
+
if (!reverseTransformedNames.has(name)) {
|
|
1445
|
+
missingFromReverse.push(name);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
if (missingFromReverse.length > 0) {
|
|
1449
|
+
throw new Error(`Private method transform count mismatch: ` +
|
|
1450
|
+
`forward transformed ${forwardTransformedNames.size} method(s), ` +
|
|
1451
|
+
`but reverse transformed ${reverseTransformedNames.size}. ` +
|
|
1452
|
+
`Missing reverse transforms for: ${missingFromReverse.join(', ')}`);
|
|
1453
|
+
}
|
|
1454
|
+
},
|
|
1455
|
+
},
|
|
1456
|
+
},
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1243
1460
|
/*
|
|
1244
1461
|
* Copyright (c) 2023, salesforce.com, inc.
|
|
1245
1462
|
* All rights reserved.
|
|
@@ -1288,6 +1505,6 @@ function LwcClassTransform(api) {
|
|
|
1288
1505
|
};
|
|
1289
1506
|
}
|
|
1290
1507
|
|
|
1291
|
-
export { LwcClassTransform as default };
|
|
1292
|
-
/** version: 9.0.
|
|
1508
|
+
export { privateMethodTransform as LwcPrivateMethodTransform, reversePrivateMethodTransform as LwcReversePrivateMethodTransform, LwcClassTransform as default };
|
|
1509
|
+
/** version: 9.0.4-alpha.0 */
|
|
1293
1510
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BabelAPI, LwcBabelPluginPass } from './types';
|
|
2
|
+
import type { PluginObj } from '@babel/core';
|
|
3
|
+
/**
|
|
4
|
+
* Standalone Babel plugin that transforms private method identifiers from
|
|
5
|
+
* `#privateMethod` to `__lwc_component_class_internal_private_privateMethod`.
|
|
6
|
+
*
|
|
7
|
+
* This must be registered BEFORE the main LWC class transform plugin so that
|
|
8
|
+
* private methods are converted to regular methods before decorator and class
|
|
9
|
+
* property processing.
|
|
10
|
+
*
|
|
11
|
+
* Uses Program > path.traverse() rather than a top-level ClassPrivateMethod visitor
|
|
12
|
+
* because the reverse transform has a ClassMethod visitor in the same Babel pass.
|
|
13
|
+
* A direct ClassPrivateMethod visitor would replace nodes that the reverse transform
|
|
14
|
+
* immediately converts back, creating an infinite loop. The manual traverse ensures
|
|
15
|
+
* all forward replacements complete before the reverse visitor sees any ClassMethod.
|
|
16
|
+
*/
|
|
17
|
+
export default function privateMethodTransform({ types: t, }: BabelAPI): PluginObj<LwcBabelPluginPass>;
|
|
18
|
+
//# sourceMappingURL=private-method-transform.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BabelAPI, LwcBabelPluginPass } from './types';
|
|
2
|
+
import type { PluginObj } from '@babel/core';
|
|
3
|
+
/**
|
|
4
|
+
* Standalone Babel plugin that reverses the private method transformation by converting
|
|
5
|
+
* methods with prefix {@link PRIVATE_METHOD_PREFIX} back to ClassPrivateMethod nodes,
|
|
6
|
+
* and restoring prefixed MemberExpression properties back to PrivateName nodes.
|
|
7
|
+
*
|
|
8
|
+
* This must be registered AFTER @babel/plugin-transform-class-properties so that
|
|
9
|
+
* class properties are fully transformed before private methods are restored.
|
|
10
|
+
*
|
|
11
|
+
* Round-trip parity: to match {@link ./private-method-transform.ts}, this transform must copy the same
|
|
12
|
+
* properties from ClassMethod onto ClassPrivateMethod when present: returnType, typeParameters, loc,
|
|
13
|
+
* abstract, access, accessibility, optional, override (plus async, generator, computed from the builder).
|
|
14
|
+
*
|
|
15
|
+
* @see {@link ./private-method-transform.ts} for original transformation
|
|
16
|
+
*/
|
|
17
|
+
export default function reversePrivateMethodTransform({ types: t, }: BabelAPI): PluginObj<LwcBabelPluginPass>;
|
|
18
|
+
//# sourceMappingURL=reverse-private-method-transform.d.ts.map
|
package/dist/utils.d.ts
CHANGED
|
@@ -21,5 +21,12 @@ declare function getEngineImportSpecifiers(path: NodePath): ImportSpecifier[];
|
|
|
21
21
|
declare function handleError(source: NodePath<types.Node>, decoratorErrorOpts: DecoratorErrorOptions, state: LwcBabelPluginPass): void;
|
|
22
22
|
declare function incrementMetricCounter(metric: CompilerMetrics, state: LwcBabelPluginPass): void;
|
|
23
23
|
declare function isErrorRecoveryMode(state: LwcBabelPluginPass): boolean;
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Copies optional metadata properties between ClassMethod and ClassPrivateMethod nodes.
|
|
26
|
+
* These properties are not accepted by the t.classMethod() / t.classPrivateMethod() builders,
|
|
27
|
+
* so they must be transferred manually after node creation. Both the forward and reverse
|
|
28
|
+
* private-method transforms use this to maintain round-trip parity.
|
|
29
|
+
*/
|
|
30
|
+
declare function copyMethodMetadata(source: types.ClassMethod | types.ClassPrivateMethod, target: types.ClassMethod | types.ClassPrivateMethod): void;
|
|
31
|
+
export { isClassMethod, isGetterClassMethod, isSetterClassMethod, getEngineImportSpecifiers, handleError, incrementMetricCounter, isErrorRecoveryMode, copyMethodMetadata, };
|
|
25
32
|
//# sourceMappingURL=utils.d.ts.map
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"You can safely modify dependencies, devDependencies, keywords, etc., but other props will be overwritten."
|
|
5
5
|
],
|
|
6
6
|
"name": "@lwc/babel-plugin-component",
|
|
7
|
-
"version": "9.0.
|
|
7
|
+
"version": "9.0.4-alpha.0",
|
|
8
8
|
"description": "Babel plugin to transform a LWC module",
|
|
9
9
|
"keywords": [
|
|
10
10
|
"lwc"
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@babel/helper-module-imports": "7.28.6",
|
|
55
|
-
"@lwc/errors": "9.0.
|
|
56
|
-
"@lwc/shared": "9.0.
|
|
55
|
+
"@lwc/errors": "9.0.4-alpha.0",
|
|
56
|
+
"@lwc/shared": "9.0.4-alpha.0",
|
|
57
57
|
"line-column": "~1.0.2"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|