@lwc/babel-plugin-component 2.42.0 → 2.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/index.cjs.js +1145 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.js +1143 -0
- package/dist/index.js.map +1 -0
- package/package.json +25 -8
- package/types/compiler-version-number.d.ts +3 -0
- package/types/component.d.ts +3 -0
- package/types/constants.d.ts +26 -0
- package/types/decorators/api/index.d.ts +8 -0
- package/types/decorators/api/shared.d.ts +3 -0
- package/types/decorators/api/transform.d.ts +6 -0
- package/types/decorators/api/validate.d.ts +2 -0
- package/types/decorators/index.d.ts +21 -0
- package/types/decorators/track/index.d.ts +10 -0
- package/types/decorators/types.d.ts +20 -0
- package/types/decorators/wire/index.d.ts +8 -0
- package/types/decorators/wire/shared.d.ts +3 -0
- package/types/decorators/wire/transform.d.ts +4 -0
- package/types/decorators/wire/validate.d.ts +2 -0
- package/types/dedupe-imports.d.ts +4 -0
- package/types/dynamic-imports.d.ts +3 -0
- package/types/index.d.ts +9 -0
- package/types/scope-css-imports.d.ts +3 -0
- package/types/types.d.ts +18 -0
- package/types/utils.d.ts +21 -0
- package/src/compiler-version-number.js +0 -33
- package/src/component.js +0 -89
- package/src/constants.js +0 -67
- package/src/decorators/api/index.js +0 -18
- package/src/decorators/api/shared.js +0 -17
- package/src/decorators/api/transform.js +0 -95
- package/src/decorators/api/validate.js +0 -160
- package/src/decorators/index.js +0 -271
- package/src/decorators/track/index.js +0 -49
- package/src/decorators/wire/index.js +0 -18
- package/src/decorators/wire/shared.js +0 -17
- package/src/decorators/wire/transform.js +0 -261
- package/src/decorators/wire/validate.js +0 -100
- package/src/dedupe-imports.js +0 -56
- package/src/dynamic-imports.js +0 -70
- package/src/index.d.ts +0 -10
- package/src/index.js +0 -77
- package/src/scope-css-imports.js +0 -23
- package/src/utils.js +0 -116
package/dist/index.js
ADDED
|
@@ -0,0 +1,1143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2023 salesforce.com, inc.
|
|
3
|
+
*/
|
|
4
|
+
import { extname, basename } from 'path';
|
|
5
|
+
import { addNamed, addDefault } from '@babel/helper-module-imports';
|
|
6
|
+
import { generateErrorMessage, DecoratorErrors, LWCClassErrors } from '@lwc/errors';
|
|
7
|
+
import lineColumn from 'line-column';
|
|
8
|
+
import { LWC_VERSION_COMMENT } from '@lwc/shared';
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
12
|
+
* All rights reserved.
|
|
13
|
+
* SPDX-License-Identifier: MIT
|
|
14
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
15
|
+
*/
|
|
16
|
+
// This set is for attributes that have a camel cased property name
|
|
17
|
+
// For example, div.tabIndex.
|
|
18
|
+
// We do not want users to define @api properties with these names
|
|
19
|
+
// Because the template will never call them. It'll always call the camel
|
|
20
|
+
// cased version.
|
|
21
|
+
const AMBIGUOUS_PROP_SET = new Map([
|
|
22
|
+
['bgcolor', 'bgColor'],
|
|
23
|
+
['accesskey', 'accessKey'],
|
|
24
|
+
['contenteditable', 'contentEditable'],
|
|
25
|
+
['contextmenu', 'contextMenu'],
|
|
26
|
+
['tabindex', 'tabIndex'],
|
|
27
|
+
['maxlength', 'maxLength'],
|
|
28
|
+
['maxvalue', 'maxValue'],
|
|
29
|
+
]);
|
|
30
|
+
// This set is for attributes that can never be defined
|
|
31
|
+
// by users on their components.
|
|
32
|
+
// We throw for these.
|
|
33
|
+
const DISALLOWED_PROP_SET = new Set(['is', 'class', 'slot', 'style']);
|
|
34
|
+
const LWC_PACKAGE_ALIAS = 'lwc';
|
|
35
|
+
const LWC_PACKAGE_EXPORTS = {
|
|
36
|
+
BASE_COMPONENT: 'LightningElement',
|
|
37
|
+
API_DECORATOR: 'api',
|
|
38
|
+
TRACK_DECORATOR: 'track',
|
|
39
|
+
WIRE_DECORATOR: 'wire',
|
|
40
|
+
};
|
|
41
|
+
const LWC_COMPONENT_PROPERTIES = {
|
|
42
|
+
PUBLIC_PROPS: 'publicProps',
|
|
43
|
+
PUBLIC_METHODS: 'publicMethods',
|
|
44
|
+
WIRE: 'wire',
|
|
45
|
+
TRACK: 'track',
|
|
46
|
+
};
|
|
47
|
+
const DECORATOR_TYPES = {
|
|
48
|
+
PROPERTY: 'property',
|
|
49
|
+
GETTER: 'getter',
|
|
50
|
+
SETTER: 'setter',
|
|
51
|
+
METHOD: 'method',
|
|
52
|
+
};
|
|
53
|
+
const REGISTER_COMPONENT_ID = 'registerComponent';
|
|
54
|
+
const REGISTER_DECORATORS_ID = 'registerDecorators';
|
|
55
|
+
const TEMPLATE_KEY = 'tmpl';
|
|
56
|
+
const COMPONENT_NAME_KEY = 'sel';
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
60
|
+
* All rights reserved.
|
|
61
|
+
* SPDX-License-Identifier: MIT
|
|
62
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
63
|
+
*/
|
|
64
|
+
function getBaseName(classPath) {
|
|
65
|
+
const ext = extname(classPath);
|
|
66
|
+
return basename(classPath, ext);
|
|
67
|
+
}
|
|
68
|
+
function importDefaultTemplate(path, state) {
|
|
69
|
+
const { filename } = state.file.opts;
|
|
70
|
+
const componentName = getBaseName(filename);
|
|
71
|
+
return addDefault(path, `./${componentName}.html`, {
|
|
72
|
+
nameHint: TEMPLATE_KEY,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function needsComponentRegistration(path) {
|
|
76
|
+
return ((path.isIdentifier() && path.node.name !== 'undefined' && path.node.name !== 'null') ||
|
|
77
|
+
path.isCallExpression() ||
|
|
78
|
+
path.isClassDeclaration() ||
|
|
79
|
+
path.isConditionalExpression());
|
|
80
|
+
}
|
|
81
|
+
function getComponentRegisteredName(t, state) {
|
|
82
|
+
const { namespace, name } = state.opts;
|
|
83
|
+
const componentName = namespace && name ? `${namespace}-${name}` : '';
|
|
84
|
+
return t.stringLiteral(componentName);
|
|
85
|
+
}
|
|
86
|
+
function component ({ types: t }) {
|
|
87
|
+
function createRegisterComponent(declarationPath, state) {
|
|
88
|
+
const registerComponentId = addNamed(declarationPath, REGISTER_COMPONENT_ID, LWC_PACKAGE_ALIAS);
|
|
89
|
+
const templateIdentifier = importDefaultTemplate(declarationPath, state);
|
|
90
|
+
const statementPath = declarationPath.getStatementParent();
|
|
91
|
+
const componentRegisteredName = getComponentRegisteredName(t, state);
|
|
92
|
+
let node = declarationPath.node;
|
|
93
|
+
if (declarationPath.isClassDeclaration()) {
|
|
94
|
+
const hasIdentifier = t.isIdentifier(node.id);
|
|
95
|
+
if (hasIdentifier) {
|
|
96
|
+
statementPath.insertBefore(node);
|
|
97
|
+
node = node.id;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// if it does not have an id, we can treat it as a ClassExpression
|
|
101
|
+
t.toExpression(node);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return t.callExpression(registerComponentId, [
|
|
105
|
+
node,
|
|
106
|
+
t.objectExpression([
|
|
107
|
+
t.objectProperty(t.identifier(TEMPLATE_KEY), templateIdentifier),
|
|
108
|
+
t.objectProperty(t.identifier(COMPONENT_NAME_KEY), componentRegisteredName),
|
|
109
|
+
]),
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
ExportDefaultDeclaration(path, state) {
|
|
114
|
+
const implicitResolution = !state.opts.isExplicitImport;
|
|
115
|
+
if (implicitResolution) {
|
|
116
|
+
const declaration = path.get('declaration');
|
|
117
|
+
if (needsComponentRegistration(declaration)) {
|
|
118
|
+
declaration.replaceWith(createRegisterComponent(declaration, state));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/*
|
|
126
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
127
|
+
* All rights reserved.
|
|
128
|
+
* SPDX-License-Identifier: MIT
|
|
129
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
130
|
+
*/
|
|
131
|
+
function isClassMethod(classMethod, properties = {}) {
|
|
132
|
+
const { kind = 'method', name } = properties;
|
|
133
|
+
return (classMethod.isClassMethod({ kind }) &&
|
|
134
|
+
(!name || classMethod.get('key').isIdentifier({ name })) &&
|
|
135
|
+
(properties.static === undefined || classMethod.node.static === properties.static));
|
|
136
|
+
}
|
|
137
|
+
function isGetterClassMethod(classMethod, properties = {}) {
|
|
138
|
+
return isClassMethod(classMethod, {
|
|
139
|
+
kind: 'get',
|
|
140
|
+
name: properties.name,
|
|
141
|
+
static: properties.static,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function isSetterClassMethod(classMethod, properties = {}) {
|
|
145
|
+
return isClassMethod(classMethod, {
|
|
146
|
+
kind: 'set',
|
|
147
|
+
name: properties.name,
|
|
148
|
+
static: properties.static,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function getEngineImportsStatements(path) {
|
|
152
|
+
const programPath = path.isProgram()
|
|
153
|
+
? path
|
|
154
|
+
: path.findParent((node) => node.isProgram());
|
|
155
|
+
return programPath.get('body').filter((node) => {
|
|
156
|
+
const source = node.get('source');
|
|
157
|
+
return node.isImportDeclaration() && source.isStringLiteral({ value: LWC_PACKAGE_ALIAS });
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function getEngineImportSpecifiers(path) {
|
|
161
|
+
const imports = getEngineImportsStatements(path);
|
|
162
|
+
return (imports
|
|
163
|
+
// Flat-map the specifier list for each import statement
|
|
164
|
+
.flatMap((importStatement) => importStatement.get('specifiers'))
|
|
165
|
+
// Skip ImportDefaultSpecifier and ImportNamespaceSpecifier
|
|
166
|
+
.filter((specifier) => specifier.type === 'ImportSpecifier')
|
|
167
|
+
// Get the list of specifiers with their name
|
|
168
|
+
.map((specifier) => {
|
|
169
|
+
const imported = specifier.get('imported').node
|
|
170
|
+
.name;
|
|
171
|
+
return { name: imported, path: specifier };
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
function normalizeFilename(source) {
|
|
175
|
+
return (
|
|
176
|
+
// TODO [#3444]: use `this.filename` to get the filename
|
|
177
|
+
(source.hub &&
|
|
178
|
+
source.hub.file &&
|
|
179
|
+
source.hub.file.opts &&
|
|
180
|
+
source.hub.file.opts.filename) ||
|
|
181
|
+
null);
|
|
182
|
+
}
|
|
183
|
+
function normalizeLocation(source) {
|
|
184
|
+
const location = (source.node && (source.node.loc || source.node._loc)) || null;
|
|
185
|
+
if (!location) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const code = source.hub.getCode();
|
|
189
|
+
if (!code) {
|
|
190
|
+
return {
|
|
191
|
+
line: location.start.line,
|
|
192
|
+
column: location.start.column,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const lineFinder = lineColumn(code);
|
|
196
|
+
const startOffset = lineFinder.toIndex(location.start.line, location.start.column + 1);
|
|
197
|
+
const endOffset = lineFinder.toIndex(location.end.line, location.end.column) + 1;
|
|
198
|
+
const length = endOffset - startOffset;
|
|
199
|
+
return {
|
|
200
|
+
line: location.start.line,
|
|
201
|
+
column: location.start.column,
|
|
202
|
+
start: startOffset,
|
|
203
|
+
length,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function generateError(source, { errorInfo, messageArgs }) {
|
|
207
|
+
const message = generateErrorMessage(errorInfo, messageArgs);
|
|
208
|
+
const error = source.buildCodeFrameError(message);
|
|
209
|
+
error.filename = normalizeFilename(source);
|
|
210
|
+
error.loc = normalizeLocation(source);
|
|
211
|
+
error.lwcCode = errorInfo && errorInfo.code;
|
|
212
|
+
return error;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/*
|
|
216
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
217
|
+
* All rights reserved.
|
|
218
|
+
* SPDX-License-Identifier: MIT
|
|
219
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
220
|
+
*/
|
|
221
|
+
const { API_DECORATOR: API_DECORATOR$2 } = LWC_PACKAGE_EXPORTS;
|
|
222
|
+
function isApiDecorator(decorator) {
|
|
223
|
+
return decorator.name === API_DECORATOR$2;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
228
|
+
* All rights reserved.
|
|
229
|
+
* SPDX-License-Identifier: MIT
|
|
230
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
231
|
+
*/
|
|
232
|
+
const { TRACK_DECORATOR: TRACK_DECORATOR$2 } = LWC_PACKAGE_EXPORTS;
|
|
233
|
+
function validateConflict(path, decorators) {
|
|
234
|
+
const isPublicFieldTracked = decorators.some((decorator) => decorator.name === TRACK_DECORATOR$2 &&
|
|
235
|
+
decorator.path.parentPath.node === path.parentPath.node);
|
|
236
|
+
if (isPublicFieldTracked) {
|
|
237
|
+
throw generateError(path, {
|
|
238
|
+
errorInfo: DecoratorErrors.API_AND_TRACK_DECORATOR_CONFLICT,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function isBooleanPropDefaultTrue(property) {
|
|
243
|
+
const propertyValue = property.node.value;
|
|
244
|
+
return propertyValue && propertyValue.type === 'BooleanLiteral' && propertyValue.value;
|
|
245
|
+
}
|
|
246
|
+
function validatePropertyValue(property) {
|
|
247
|
+
if (isBooleanPropDefaultTrue(property)) {
|
|
248
|
+
throw generateError(property, {
|
|
249
|
+
errorInfo: DecoratorErrors.INVALID_BOOLEAN_PUBLIC_PROPERTY,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function validatePropertyName(property) {
|
|
254
|
+
if (property.node.computed) {
|
|
255
|
+
throw generateError(property, {
|
|
256
|
+
errorInfo: DecoratorErrors.PROPERTY_CANNOT_BE_COMPUTED,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const propertyName = property.get('key.name').node;
|
|
260
|
+
if (propertyName === 'part') {
|
|
261
|
+
throw generateError(property, {
|
|
262
|
+
errorInfo: DecoratorErrors.PROPERTY_NAME_PART_IS_RESERVED,
|
|
263
|
+
messageArgs: [propertyName],
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
else if (propertyName.startsWith('on')) {
|
|
267
|
+
throw generateError(property, {
|
|
268
|
+
errorInfo: DecoratorErrors.PROPERTY_NAME_CANNOT_START_WITH_ON,
|
|
269
|
+
messageArgs: [propertyName],
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
else if (propertyName.startsWith('data') && propertyName.length > 4) {
|
|
273
|
+
throw generateError(property, {
|
|
274
|
+
errorInfo: DecoratorErrors.PROPERTY_NAME_CANNOT_START_WITH_DATA,
|
|
275
|
+
messageArgs: [propertyName],
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
else if (DISALLOWED_PROP_SET.has(propertyName)) {
|
|
279
|
+
throw generateError(property, {
|
|
280
|
+
errorInfo: DecoratorErrors.PROPERTY_NAME_IS_RESERVED,
|
|
281
|
+
messageArgs: [propertyName],
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
else if (AMBIGUOUS_PROP_SET.has(propertyName)) {
|
|
285
|
+
const camelCased = AMBIGUOUS_PROP_SET.get(propertyName);
|
|
286
|
+
throw generateError(property, {
|
|
287
|
+
errorInfo: DecoratorErrors.PROPERTY_NAME_IS_AMBIGUOUS,
|
|
288
|
+
messageArgs: [propertyName, camelCased],
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function validateSingleApiDecoratorOnSetterGetterPair(decorators) {
|
|
293
|
+
// keep track of visited class methods
|
|
294
|
+
const visitedMethods = new Set();
|
|
295
|
+
decorators.forEach((decorator) => {
|
|
296
|
+
const { path, decoratedNodeType } = decorator;
|
|
297
|
+
// since we are validating get/set we only look at @api methods
|
|
298
|
+
if (isApiDecorator(decorator) &&
|
|
299
|
+
(decoratedNodeType === DECORATOR_TYPES.GETTER ||
|
|
300
|
+
decoratedNodeType === DECORATOR_TYPES.SETTER)) {
|
|
301
|
+
const methodPath = path.parentPath;
|
|
302
|
+
const methodName = methodPath.get('key.name').node;
|
|
303
|
+
if (visitedMethods.has(methodName)) {
|
|
304
|
+
throw generateError(methodPath, {
|
|
305
|
+
errorInfo: DecoratorErrors.SINGLE_DECORATOR_ON_SETTER_GETTER_PAIR,
|
|
306
|
+
messageArgs: [methodName],
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
visitedMethods.add(methodName);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
function validateUniqueness(decorators) {
|
|
314
|
+
const apiDecorators = decorators.filter(isApiDecorator);
|
|
315
|
+
for (let i = 0; i < apiDecorators.length; i++) {
|
|
316
|
+
const { path: currentPath, type: currentType } = apiDecorators[i];
|
|
317
|
+
const currentPropertyName = currentPath.parentPath.get('key.name').node;
|
|
318
|
+
for (let j = 0; j < apiDecorators.length; j++) {
|
|
319
|
+
const { path: comparePath, type: compareType } = apiDecorators[j];
|
|
320
|
+
const comparePropertyName = comparePath.parentPath.get('key.name')
|
|
321
|
+
.node;
|
|
322
|
+
// We will throw if the considered properties have the same name, and when their
|
|
323
|
+
// are not part of a pair of getter/setter.
|
|
324
|
+
const haveSameName = currentPropertyName === comparePropertyName;
|
|
325
|
+
const isDifferentProperty = currentPath !== comparePath;
|
|
326
|
+
const isGetterSetterPair = (currentType === DECORATOR_TYPES.GETTER &&
|
|
327
|
+
compareType === DECORATOR_TYPES.SETTER) ||
|
|
328
|
+
(currentType === DECORATOR_TYPES.SETTER && compareType === DECORATOR_TYPES.GETTER);
|
|
329
|
+
if (haveSameName && isDifferentProperty && !isGetterSetterPair) {
|
|
330
|
+
throw generateError(comparePath, {
|
|
331
|
+
errorInfo: DecoratorErrors.DUPLICATE_API_PROPERTY,
|
|
332
|
+
messageArgs: [currentPropertyName],
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function validate$3(decorators) {
|
|
339
|
+
const apiDecorators = decorators.filter(isApiDecorator);
|
|
340
|
+
if (apiDecorators.length === 0) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
apiDecorators.forEach(({ path, decoratedNodeType }) => {
|
|
344
|
+
validateConflict(path, decorators);
|
|
345
|
+
if (decoratedNodeType !== DECORATOR_TYPES.METHOD) {
|
|
346
|
+
const property = path.parentPath;
|
|
347
|
+
validatePropertyName(property);
|
|
348
|
+
validatePropertyValue(property);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
validateSingleApiDecoratorOnSetterGetterPair(decorators);
|
|
352
|
+
validateUniqueness(decorators);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const { PUBLIC_PROPS, PUBLIC_METHODS } = LWC_COMPONENT_PROPERTIES;
|
|
356
|
+
const PUBLIC_PROP_BIT_MASK = {
|
|
357
|
+
PROPERTY: 0,
|
|
358
|
+
GETTER: 1,
|
|
359
|
+
SETTER: 2,
|
|
360
|
+
};
|
|
361
|
+
function getPropertyBitmask(type) {
|
|
362
|
+
switch (type) {
|
|
363
|
+
case DECORATOR_TYPES.GETTER:
|
|
364
|
+
return PUBLIC_PROP_BIT_MASK.GETTER;
|
|
365
|
+
case DECORATOR_TYPES.SETTER:
|
|
366
|
+
return PUBLIC_PROP_BIT_MASK.SETTER;
|
|
367
|
+
default:
|
|
368
|
+
return PUBLIC_PROP_BIT_MASK.PROPERTY;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function getSiblingGetSetPairType(propertyName, type, classBodyItems) {
|
|
372
|
+
const siblingKind = type === DECORATOR_TYPES.GETTER ? 'set' : 'get';
|
|
373
|
+
const siblingNode = classBodyItems.find((classBodyItem) => {
|
|
374
|
+
const isClassMethod = classBodyItem.isClassMethod({ kind: siblingKind });
|
|
375
|
+
const isSamePropertyName = classBodyItem.node.key.name ===
|
|
376
|
+
propertyName;
|
|
377
|
+
return isClassMethod && isSamePropertyName;
|
|
378
|
+
});
|
|
379
|
+
if (siblingNode) {
|
|
380
|
+
return siblingKind === 'get' ? DECORATOR_TYPES.GETTER : DECORATOR_TYPES.SETTER;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function computePublicPropsConfig(publicPropertyMetas, classBodyItems) {
|
|
384
|
+
return publicPropertyMetas.reduce((acc, { propertyName, decoratedNodeType }) => {
|
|
385
|
+
if (!(propertyName in acc)) {
|
|
386
|
+
acc[propertyName] = {};
|
|
387
|
+
}
|
|
388
|
+
acc[propertyName].config |= getPropertyBitmask(decoratedNodeType);
|
|
389
|
+
if (decoratedNodeType === DECORATOR_TYPES.GETTER ||
|
|
390
|
+
decoratedNodeType === DECORATOR_TYPES.SETTER) {
|
|
391
|
+
// With the latest decorator spec, only one of the getter/setter pair needs a decorator.
|
|
392
|
+
// We need to add the proper bitmask for the sibling getter/setter if it exists.
|
|
393
|
+
const pairType = getSiblingGetSetPairType(propertyName, decoratedNodeType, classBodyItems);
|
|
394
|
+
if (pairType) {
|
|
395
|
+
acc[propertyName].config |= getPropertyBitmask(pairType);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return acc;
|
|
399
|
+
}, {});
|
|
400
|
+
}
|
|
401
|
+
function transform$2(t, decoratorMetas, classBodyItems) {
|
|
402
|
+
const objectProperties = [];
|
|
403
|
+
const apiDecoratorMetas = decoratorMetas.filter(isApiDecorator);
|
|
404
|
+
const publicPropertyMetas = apiDecoratorMetas.filter(({ decoratedNodeType }) => decoratedNodeType !== DECORATOR_TYPES.METHOD);
|
|
405
|
+
if (publicPropertyMetas.length) {
|
|
406
|
+
const propsConfig = computePublicPropsConfig(publicPropertyMetas, classBodyItems);
|
|
407
|
+
objectProperties.push(t.objectProperty(t.identifier(PUBLIC_PROPS), t.valueToNode(propsConfig)));
|
|
408
|
+
}
|
|
409
|
+
const publicMethodMetas = apiDecoratorMetas.filter(({ decoratedNodeType }) => decoratedNodeType === DECORATOR_TYPES.METHOD);
|
|
410
|
+
if (publicMethodMetas.length) {
|
|
411
|
+
const methodNames = publicMethodMetas.map(({ propertyName }) => propertyName);
|
|
412
|
+
objectProperties.push(t.objectProperty(t.identifier(PUBLIC_METHODS), t.valueToNode(methodNames)));
|
|
413
|
+
}
|
|
414
|
+
return objectProperties;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/*
|
|
418
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
419
|
+
* All rights reserved.
|
|
420
|
+
* SPDX-License-Identifier: MIT
|
|
421
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
422
|
+
*/
|
|
423
|
+
const { API_DECORATOR: API_DECORATOR$1 } = LWC_PACKAGE_EXPORTS;
|
|
424
|
+
var api = {
|
|
425
|
+
name: API_DECORATOR$1,
|
|
426
|
+
validate: validate$3,
|
|
427
|
+
transform: transform$2,
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
/*
|
|
431
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
432
|
+
* All rights reserved.
|
|
433
|
+
* SPDX-License-Identifier: MIT
|
|
434
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
435
|
+
*/
|
|
436
|
+
const { WIRE_DECORATOR: WIRE_DECORATOR$2 } = LWC_PACKAGE_EXPORTS;
|
|
437
|
+
function isWireDecorator(decorator) {
|
|
438
|
+
return decorator.name === WIRE_DECORATOR$2;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const { TRACK_DECORATOR: TRACK_DECORATOR$1, WIRE_DECORATOR: WIRE_DECORATOR$1, API_DECORATOR } = LWC_PACKAGE_EXPORTS;
|
|
442
|
+
function validateWireParameters(path) {
|
|
443
|
+
const [id, config] = path.get('expression.arguments');
|
|
444
|
+
if (!id) {
|
|
445
|
+
throw generateError(path, {
|
|
446
|
+
errorInfo: DecoratorErrors.ADAPTER_SHOULD_BE_FIRST_PARAMETER,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
const isIdentifier = id.isIdentifier();
|
|
450
|
+
const isMemberExpression = id.isMemberExpression();
|
|
451
|
+
if (!isIdentifier && !isMemberExpression) {
|
|
452
|
+
throw generateError(id, {
|
|
453
|
+
errorInfo: DecoratorErrors.FUNCTION_IDENTIFIER_SHOULD_BE_FIRST_PARAMETER,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
if (id.isMemberExpression({ computed: true })) {
|
|
457
|
+
throw generateError(id, {
|
|
458
|
+
errorInfo: DecoratorErrors.FUNCTION_IDENTIFIER_CANNOT_HAVE_COMPUTED_PROPS,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
// TODO [#3444]: improve member expression computed typechecking
|
|
462
|
+
// @ts-ignore
|
|
463
|
+
if (isMemberExpression && !id.get('object').isIdentifier()) {
|
|
464
|
+
throw generateError(id, {
|
|
465
|
+
errorInfo: DecoratorErrors.FUNCTION_IDENTIFIER_CANNOT_HAVE_NESTED_MEMBER_EXRESSIONS,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
// TODO [#3444]: improve member expression computed typechecking
|
|
469
|
+
// Ensure wire adapter is imported (check for member expression or identifier)
|
|
470
|
+
// @ts-ignore
|
|
471
|
+
const wireBinding = isMemberExpression ? id.node.object.name : id.node.name;
|
|
472
|
+
if (!path.scope.getBinding(wireBinding)) {
|
|
473
|
+
throw generateError(id, {
|
|
474
|
+
errorInfo: DecoratorErrors.WIRE_ADAPTER_SHOULD_BE_IMPORTED,
|
|
475
|
+
messageArgs: [id.node.name],
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
// ensure wire adapter is a first parameter
|
|
479
|
+
if (wireBinding &&
|
|
480
|
+
!path.scope.getBinding(wireBinding).path.isImportSpecifier() &&
|
|
481
|
+
!path.scope.getBinding(wireBinding).path.isImportDefaultSpecifier()) {
|
|
482
|
+
throw generateError(id, {
|
|
483
|
+
errorInfo: DecoratorErrors.IMPORTED_FUNCTION_IDENTIFIER_SHOULD_BE_FIRST_PARAMETER,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
if (config && !config.isObjectExpression()) {
|
|
487
|
+
throw generateError(config, {
|
|
488
|
+
errorInfo: DecoratorErrors.CONFIG_OBJECT_SHOULD_BE_SECOND_PARAMETER,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function validateUsageWithOtherDecorators(path, decorators) {
|
|
493
|
+
decorators.forEach((decorator) => {
|
|
494
|
+
if (path !== decorator.path &&
|
|
495
|
+
decorator.name === WIRE_DECORATOR$1 &&
|
|
496
|
+
decorator.path.parentPath.node === path.parentPath.node) {
|
|
497
|
+
throw generateError(path, {
|
|
498
|
+
errorInfo: DecoratorErrors.ONE_WIRE_DECORATOR_ALLOWED,
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
if ((decorator.name === API_DECORATOR || decorator.name === TRACK_DECORATOR$1) &&
|
|
502
|
+
decorator.path.parentPath.node === path.parentPath.node) {
|
|
503
|
+
throw generateError(path, {
|
|
504
|
+
errorInfo: DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR,
|
|
505
|
+
messageArgs: [decorator.name],
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
function validate$2(decorators) {
|
|
511
|
+
decorators.filter(isWireDecorator).forEach(({ path }) => {
|
|
512
|
+
validateUsageWithOtherDecorators(path, decorators);
|
|
513
|
+
validateWireParameters(path);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const WIRE_PARAM_PREFIX = '$';
|
|
518
|
+
const WIRE_CONFIG_ARG_NAME = '$cmp';
|
|
519
|
+
function isObservedProperty(configProperty) {
|
|
520
|
+
const propertyValue = configProperty.get('value');
|
|
521
|
+
return (propertyValue.isStringLiteral() && propertyValue.node.value.startsWith(WIRE_PARAM_PREFIX));
|
|
522
|
+
}
|
|
523
|
+
function getWiredStatic(wireConfig) {
|
|
524
|
+
return wireConfig
|
|
525
|
+
.get('properties')
|
|
526
|
+
.filter((property) => !isObservedProperty(property))
|
|
527
|
+
.map((path) => path.node);
|
|
528
|
+
}
|
|
529
|
+
function getWiredParams(t, wireConfig) {
|
|
530
|
+
return wireConfig
|
|
531
|
+
.get('properties')
|
|
532
|
+
.filter((property) => isObservedProperty(property))
|
|
533
|
+
.map((path) => {
|
|
534
|
+
// Need to clone deep the observed property to remove the param prefix
|
|
535
|
+
const clonedProperty = t.cloneDeep(path.node);
|
|
536
|
+
clonedProperty.value.value = clonedProperty.value.value.slice(1);
|
|
537
|
+
return clonedProperty;
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
function getGeneratedConfig(t, wiredValue) {
|
|
541
|
+
let counter = 0;
|
|
542
|
+
const configBlockBody = [];
|
|
543
|
+
const configProps = [];
|
|
544
|
+
const generateParameterConfigValue = (memberExprPaths) => {
|
|
545
|
+
// Note: When memberExprPaths ($foo.bar) has an invalid identifier (eg: foo..bar, foo.bar[3])
|
|
546
|
+
// it should (ideally) resolve in a compilation error during validation phase.
|
|
547
|
+
// This is not possible due that platform components may have a param definition which is invalid
|
|
548
|
+
// but passes compilation, and throwing at compile time would break such components.
|
|
549
|
+
// In such cases where the param does not have proper notation, the config generated will use the bracket
|
|
550
|
+
// notation to match the current behavior (that most likely end up resolving that param as undefined).
|
|
551
|
+
const isInvalidMemberExpr = memberExprPaths.some((maybeIdentifier) => !(t.isValidES3Identifier(maybeIdentifier) && maybeIdentifier.length > 0));
|
|
552
|
+
const memberExprPropertyGen = !isInvalidMemberExpr
|
|
553
|
+
? t.identifier
|
|
554
|
+
: t.StringLiteral;
|
|
555
|
+
if (memberExprPaths.length === 1) {
|
|
556
|
+
return {
|
|
557
|
+
configValueExpression: t.memberExpression(t.identifier(WIRE_CONFIG_ARG_NAME), memberExprPropertyGen(memberExprPaths[0])),
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
const varName = 'v' + ++counter;
|
|
561
|
+
const varDeclaration = t.variableDeclaration('let', [
|
|
562
|
+
t.variableDeclarator(t.identifier(varName), t.memberExpression(t.identifier(WIRE_CONFIG_ARG_NAME), memberExprPropertyGen(memberExprPaths[0]), isInvalidMemberExpr)),
|
|
563
|
+
]);
|
|
564
|
+
// Results in: v != null && ... (v = v.i) != null && ... (v = v.(n-1)) != null
|
|
565
|
+
let conditionTest = t.binaryExpression('!=', t.identifier(varName), t.nullLiteral());
|
|
566
|
+
for (let i = 1, n = memberExprPaths.length; i < n - 1; i++) {
|
|
567
|
+
const nextPropValue = t.assignmentExpression('=', t.identifier(varName), t.memberExpression(t.identifier(varName), memberExprPropertyGen(memberExprPaths[i]), isInvalidMemberExpr));
|
|
568
|
+
conditionTest = t.logicalExpression('&&', conditionTest, t.binaryExpression('!=', nextPropValue, t.nullLiteral()));
|
|
569
|
+
}
|
|
570
|
+
// conditionTest ? v.n : undefined
|
|
571
|
+
const configValueExpression = t.conditionalExpression(conditionTest, t.memberExpression(t.identifier(varName), memberExprPropertyGen(memberExprPaths[memberExprPaths.length - 1]), isInvalidMemberExpr), t.identifier('undefined'));
|
|
572
|
+
return {
|
|
573
|
+
varDeclaration,
|
|
574
|
+
configValueExpression,
|
|
575
|
+
};
|
|
576
|
+
};
|
|
577
|
+
if (wiredValue.static) {
|
|
578
|
+
Array.prototype.push.apply(configProps, wiredValue.static);
|
|
579
|
+
}
|
|
580
|
+
if (wiredValue.params) {
|
|
581
|
+
wiredValue.params.forEach((param) => {
|
|
582
|
+
const memberExprPaths = param.value.value.split('.');
|
|
583
|
+
const paramConfigValue = generateParameterConfigValue(memberExprPaths);
|
|
584
|
+
configProps.push(t.objectProperty(param.key, paramConfigValue.configValueExpression));
|
|
585
|
+
if (paramConfigValue.varDeclaration) {
|
|
586
|
+
configBlockBody.push(paramConfigValue.varDeclaration);
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
configBlockBody.push(t.returnStatement(t.objectExpression(configProps)));
|
|
591
|
+
const fnExpression = t.functionExpression(null, [t.identifier(WIRE_CONFIG_ARG_NAME)], t.blockStatement(configBlockBody));
|
|
592
|
+
return t.objectProperty(t.identifier('config'), fnExpression);
|
|
593
|
+
}
|
|
594
|
+
function buildWireConfigValue(t, wiredValues) {
|
|
595
|
+
return t.objectExpression(wiredValues.map((wiredValue) => {
|
|
596
|
+
const wireConfig = [];
|
|
597
|
+
if (wiredValue.adapter) {
|
|
598
|
+
wireConfig.push(t.objectProperty(t.identifier('adapter'), wiredValue.adapter.expression));
|
|
599
|
+
}
|
|
600
|
+
if (wiredValue.params) {
|
|
601
|
+
const dynamicParamNames = wiredValue.params.map((p) => {
|
|
602
|
+
return t.stringLiteral(t.isIdentifier(p.key) ? p.key.name : p.key.value);
|
|
603
|
+
});
|
|
604
|
+
wireConfig.push(t.objectProperty(t.identifier('dynamic'), t.arrayExpression(dynamicParamNames)));
|
|
605
|
+
}
|
|
606
|
+
if (wiredValue.isClassMethod) {
|
|
607
|
+
wireConfig.push(t.objectProperty(t.identifier('method'), t.numericLiteral(1)));
|
|
608
|
+
}
|
|
609
|
+
wireConfig.push(getGeneratedConfig(t, wiredValue));
|
|
610
|
+
return t.objectProperty(t.identifier(wiredValue.propertyName), t.objectExpression(wireConfig));
|
|
611
|
+
}));
|
|
612
|
+
}
|
|
613
|
+
const SUPPORTED_VALUE_TO_TYPE_MAP = {
|
|
614
|
+
StringLiteral: 'string',
|
|
615
|
+
NumericLiteral: 'number',
|
|
616
|
+
BooleanLiteral: 'boolean',
|
|
617
|
+
};
|
|
618
|
+
const scopedReferenceLookup = (scope) => (name) => {
|
|
619
|
+
const binding = scope.getBinding(name);
|
|
620
|
+
let type;
|
|
621
|
+
let value;
|
|
622
|
+
if (binding) {
|
|
623
|
+
if (binding.kind === 'module') {
|
|
624
|
+
// Resolves module import to the name of the module imported
|
|
625
|
+
// e.g. import { foo } from 'bar' gives value 'bar' for `name == 'foo'
|
|
626
|
+
const parentPathNode = binding.path.parentPath.node;
|
|
627
|
+
if (parentPathNode && parentPathNode.source) {
|
|
628
|
+
type = 'module';
|
|
629
|
+
value = parentPathNode.source.value;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
else if (binding.kind === 'const') {
|
|
633
|
+
// Resolves `const foo = 'text';` references to value 'text', where `name == 'foo'`
|
|
634
|
+
const init = binding.path.node.init;
|
|
635
|
+
if (init &&
|
|
636
|
+
SUPPORTED_VALUE_TO_TYPE_MAP[init.type]) {
|
|
637
|
+
type =
|
|
638
|
+
SUPPORTED_VALUE_TO_TYPE_MAP[init.type];
|
|
639
|
+
value = init
|
|
640
|
+
.value;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
type,
|
|
646
|
+
value,
|
|
647
|
+
};
|
|
648
|
+
};
|
|
649
|
+
function transform$1(t, decoratorMetas) {
|
|
650
|
+
const objectProperties = [];
|
|
651
|
+
const wiredValues = decoratorMetas.filter(isWireDecorator).map(({ path }) => {
|
|
652
|
+
const [id, config] = path.get('expression.arguments');
|
|
653
|
+
const propertyName = path.parentPath.get('key.name').node;
|
|
654
|
+
const isClassMethod = path.parentPath.isClassMethod({
|
|
655
|
+
kind: 'method',
|
|
656
|
+
});
|
|
657
|
+
const wiredValue = {
|
|
658
|
+
propertyName,
|
|
659
|
+
isClassMethod,
|
|
660
|
+
};
|
|
661
|
+
if (config) {
|
|
662
|
+
wiredValue.static = getWiredStatic(config);
|
|
663
|
+
wiredValue.params = getWiredParams(t, config);
|
|
664
|
+
}
|
|
665
|
+
const referenceLookup = scopedReferenceLookup(path.scope);
|
|
666
|
+
const isMemberExpression = id.isMemberExpression();
|
|
667
|
+
const isIdentifier = id.isIdentifier();
|
|
668
|
+
if (isIdentifier || isMemberExpression) {
|
|
669
|
+
const referenceName = isMemberExpression ? id.node.object.name : id.node.name;
|
|
670
|
+
const reference = referenceLookup(referenceName);
|
|
671
|
+
wiredValue.adapter = {
|
|
672
|
+
name: referenceName,
|
|
673
|
+
expression: t.cloneDeep(id.node),
|
|
674
|
+
reference: reference.type === 'module' ? reference.value : undefined,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
return wiredValue;
|
|
678
|
+
});
|
|
679
|
+
if (wiredValues.length) {
|
|
680
|
+
objectProperties.push(t.objectProperty(t.identifier(LWC_COMPONENT_PROPERTIES.WIRE), buildWireConfigValue(t, wiredValues)));
|
|
681
|
+
}
|
|
682
|
+
return objectProperties;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/*
|
|
686
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
687
|
+
* All rights reserved.
|
|
688
|
+
* SPDX-License-Identifier: MIT
|
|
689
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
690
|
+
*/
|
|
691
|
+
const { WIRE_DECORATOR } = LWC_PACKAGE_EXPORTS;
|
|
692
|
+
var wire = {
|
|
693
|
+
name: WIRE_DECORATOR,
|
|
694
|
+
validate: validate$2,
|
|
695
|
+
transform: transform$1,
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
/*
|
|
699
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
700
|
+
* All rights reserved.
|
|
701
|
+
* SPDX-License-Identifier: MIT
|
|
702
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
703
|
+
*/
|
|
704
|
+
const { TRACK_DECORATOR } = LWC_PACKAGE_EXPORTS;
|
|
705
|
+
const TRACK_PROPERTY_VALUE = 1;
|
|
706
|
+
function isTrackDecorator(decorator) {
|
|
707
|
+
return decorator.name === TRACK_DECORATOR;
|
|
708
|
+
}
|
|
709
|
+
function validate$1(decorators) {
|
|
710
|
+
decorators.filter(isTrackDecorator).forEach(({ path }) => {
|
|
711
|
+
if (!path.parentPath.isClassProperty()) {
|
|
712
|
+
throw generateError(path, {
|
|
713
|
+
errorInfo: DecoratorErrors.TRACK_ONLY_ALLOWED_ON_CLASS_PROPERTIES,
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
function transform(t, decoratorMetas) {
|
|
719
|
+
const objectProperties = [];
|
|
720
|
+
const trackDecoratorMetas = decoratorMetas.filter(isTrackDecorator);
|
|
721
|
+
if (trackDecoratorMetas.length) {
|
|
722
|
+
const config = trackDecoratorMetas.reduce((acc, meta) => {
|
|
723
|
+
acc[meta.propertyName] = TRACK_PROPERTY_VALUE;
|
|
724
|
+
return acc;
|
|
725
|
+
}, {});
|
|
726
|
+
objectProperties.push(t.objectProperty(t.identifier(LWC_COMPONENT_PROPERTIES.TRACK), t.valueToNode(config)));
|
|
727
|
+
}
|
|
728
|
+
return objectProperties;
|
|
729
|
+
}
|
|
730
|
+
var track = {
|
|
731
|
+
name: TRACK_DECORATOR,
|
|
732
|
+
transform,
|
|
733
|
+
validate: validate$1,
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
const DECORATOR_TRANSFORMS = [api, wire, track];
|
|
737
|
+
const AVAILABLE_DECORATORS = DECORATOR_TRANSFORMS.map((transform) => transform.name).join(', ');
|
|
738
|
+
function isLwcDecoratorName(name) {
|
|
739
|
+
return DECORATOR_TRANSFORMS.some((transform) => transform.name === name);
|
|
740
|
+
}
|
|
741
|
+
/** Returns a list of all the references to an identifier */
|
|
742
|
+
function getReferences(identifier) {
|
|
743
|
+
return identifier.scope.getBinding(identifier.node.name).referencePaths;
|
|
744
|
+
}
|
|
745
|
+
/** Returns the type of decorator depdending on the property or method if get applied to */
|
|
746
|
+
function getDecoratedNodeType(decoratorPath) {
|
|
747
|
+
const propertyOrMethod = decoratorPath.parentPath;
|
|
748
|
+
if (isClassMethod(propertyOrMethod)) {
|
|
749
|
+
return DECORATOR_TYPES.METHOD;
|
|
750
|
+
}
|
|
751
|
+
else if (isGetterClassMethod(propertyOrMethod)) {
|
|
752
|
+
return DECORATOR_TYPES.GETTER;
|
|
753
|
+
}
|
|
754
|
+
else if (isSetterClassMethod(propertyOrMethod)) {
|
|
755
|
+
return DECORATOR_TYPES.SETTER;
|
|
756
|
+
}
|
|
757
|
+
else if (propertyOrMethod.isClassProperty()) {
|
|
758
|
+
return DECORATOR_TYPES.PROPERTY;
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
throw generateError(propertyOrMethod, {
|
|
762
|
+
errorInfo: DecoratorErrors.INVALID_DECORATOR_TYPE,
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
function validateImportedLwcDecoratorUsage(engineImportSpecifiers) {
|
|
767
|
+
engineImportSpecifiers
|
|
768
|
+
.filter(({ name }) => isLwcDecoratorName(name))
|
|
769
|
+
.reduce((acc, { name, path }) => {
|
|
770
|
+
// Get a list of all the local references
|
|
771
|
+
const local = path.get('imported');
|
|
772
|
+
const references = getReferences(local).map((reference) => ({
|
|
773
|
+
name,
|
|
774
|
+
reference,
|
|
775
|
+
}));
|
|
776
|
+
return [...acc, ...references];
|
|
777
|
+
}, [])
|
|
778
|
+
.forEach(({ name, reference }) => {
|
|
779
|
+
// Get the decorator from the identifier
|
|
780
|
+
// If the the decorator is:
|
|
781
|
+
// - an identifier @track : the decorator is the parent of the identifier
|
|
782
|
+
// - a call expression @wire("foo") : the decorator is the grand-parent of the identifier
|
|
783
|
+
const decorator = reference.parentPath.isDecorator()
|
|
784
|
+
? reference.parentPath
|
|
785
|
+
: reference.parentPath.parentPath;
|
|
786
|
+
if (!decorator.isDecorator()) {
|
|
787
|
+
throw generateError(decorator, {
|
|
788
|
+
errorInfo: DecoratorErrors.IS_NOT_DECORATOR,
|
|
789
|
+
messageArgs: [name],
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
const propertyOrMethod = decorator.parentPath;
|
|
793
|
+
if (!propertyOrMethod.isClassProperty() && !propertyOrMethod.isClassMethod()) {
|
|
794
|
+
throw generateError(propertyOrMethod, {
|
|
795
|
+
errorInfo: DecoratorErrors.IS_NOT_CLASS_PROPERTY_OR_CLASS_METHOD,
|
|
796
|
+
messageArgs: [name],
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
function isImportedFromLwcSource(decoratorBinding) {
|
|
802
|
+
const bindingPath = decoratorBinding.path;
|
|
803
|
+
return (bindingPath.isImportSpecifier() &&
|
|
804
|
+
bindingPath.parent.source.value === 'lwc');
|
|
805
|
+
}
|
|
806
|
+
/** Validate the usage of decorator by calling each validation function */
|
|
807
|
+
function validate(decorators) {
|
|
808
|
+
for (const { name, path } of decorators) {
|
|
809
|
+
const binding = path.scope.getBinding(name);
|
|
810
|
+
if (binding === undefined || !isImportedFromLwcSource(binding)) {
|
|
811
|
+
throw generateInvalidDecoratorError(path);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
DECORATOR_TRANSFORMS.forEach(({ validate }) => validate(decorators));
|
|
815
|
+
}
|
|
816
|
+
/** Remove import specifiers. It also removes the import statement if the specifier list becomes empty */
|
|
817
|
+
function removeImportedDecoratorSpecifiers(engineImportSpecifiers) {
|
|
818
|
+
engineImportSpecifiers
|
|
819
|
+
.filter(({ name }) => isLwcDecoratorName(name))
|
|
820
|
+
.forEach(({ path }) => {
|
|
821
|
+
const importStatement = path.parentPath;
|
|
822
|
+
path.remove();
|
|
823
|
+
if (importStatement.get('specifiers').length === 0) {
|
|
824
|
+
importStatement.remove();
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
function generateInvalidDecoratorError(path) {
|
|
829
|
+
const expressionPath = path.get('expression');
|
|
830
|
+
const { node } = path;
|
|
831
|
+
const { expression } = node;
|
|
832
|
+
let name;
|
|
833
|
+
if (expressionPath.isIdentifier()) {
|
|
834
|
+
name = expression.name;
|
|
835
|
+
}
|
|
836
|
+
else if (expressionPath.isCallExpression()) {
|
|
837
|
+
name = expression.callee.name;
|
|
838
|
+
}
|
|
839
|
+
if (name) {
|
|
840
|
+
return generateError(path.parentPath, {
|
|
841
|
+
errorInfo: DecoratorErrors.INVALID_DECORATOR_WITH_NAME,
|
|
842
|
+
messageArgs: [name, AVAILABLE_DECORATORS, LWC_PACKAGE_ALIAS],
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
return generateError(path.parentPath, {
|
|
847
|
+
errorInfo: DecoratorErrors.INVALID_DECORATOR,
|
|
848
|
+
messageArgs: [AVAILABLE_DECORATORS, LWC_PACKAGE_ALIAS],
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
function collectDecoratorPaths(bodyItems) {
|
|
853
|
+
return bodyItems.reduce((acc, bodyItem) => {
|
|
854
|
+
const decorators = bodyItem.get('decorators');
|
|
855
|
+
if (decorators && decorators.length) {
|
|
856
|
+
acc.push(...decorators);
|
|
857
|
+
}
|
|
858
|
+
return acc;
|
|
859
|
+
}, []);
|
|
860
|
+
}
|
|
861
|
+
function getDecoratorMetadata(decoratorPath) {
|
|
862
|
+
const expressionPath = decoratorPath.get('expression');
|
|
863
|
+
let name;
|
|
864
|
+
if (expressionPath.isIdentifier()) {
|
|
865
|
+
name = expressionPath.node.name;
|
|
866
|
+
}
|
|
867
|
+
else if (expressionPath.isCallExpression()) {
|
|
868
|
+
name = expressionPath.node.callee.name;
|
|
869
|
+
}
|
|
870
|
+
else {
|
|
871
|
+
throw generateInvalidDecoratorError(decoratorPath);
|
|
872
|
+
}
|
|
873
|
+
const propertyName = decoratorPath.parent.key.name;
|
|
874
|
+
const decoratedNodeType = getDecoratedNodeType(decoratorPath);
|
|
875
|
+
return {
|
|
876
|
+
name,
|
|
877
|
+
propertyName,
|
|
878
|
+
path: decoratorPath,
|
|
879
|
+
decoratedNodeType,
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
function getMetadataObjectPropertyList(t, decoratorMetas, classBodyItems) {
|
|
883
|
+
const list = [
|
|
884
|
+
...api.transform(t, decoratorMetas, classBodyItems),
|
|
885
|
+
...track.transform(t, decoratorMetas),
|
|
886
|
+
...wire.transform(t, decoratorMetas),
|
|
887
|
+
];
|
|
888
|
+
const fieldNames = classBodyItems
|
|
889
|
+
.filter((field) => field.isClassProperty({ computed: false, static: false }))
|
|
890
|
+
.filter((field) => !field.node.decorators)
|
|
891
|
+
.map((field) => field.node.key.name);
|
|
892
|
+
if (fieldNames.length) {
|
|
893
|
+
list.push(t.objectProperty(t.identifier('fields'), t.valueToNode(fieldNames)));
|
|
894
|
+
}
|
|
895
|
+
return list;
|
|
896
|
+
}
|
|
897
|
+
function decorators({ types: t }) {
|
|
898
|
+
function createRegisterDecoratorsCallExpression(path, classExpression, props) {
|
|
899
|
+
const id = addNamed(path, REGISTER_DECORATORS_ID, LWC_PACKAGE_ALIAS);
|
|
900
|
+
return t.callExpression(id, [classExpression, t.objectExpression(props)]);
|
|
901
|
+
}
|
|
902
|
+
// Babel reinvokes visitors for node reinsertion so we use this to avoid an infinite loop.
|
|
903
|
+
const visitedClasses = new WeakSet();
|
|
904
|
+
return {
|
|
905
|
+
Class(path) {
|
|
906
|
+
const { node } = path;
|
|
907
|
+
if (visitedClasses.has(node)) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
visitedClasses.add(node);
|
|
911
|
+
const classBodyItems = path.get('body.body');
|
|
912
|
+
if (classBodyItems.length === 0) {
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
const decoratorPaths = collectDecoratorPaths(classBodyItems);
|
|
916
|
+
const decoratorMetas = decoratorPaths.map(getDecoratorMetadata);
|
|
917
|
+
validate(decoratorMetas);
|
|
918
|
+
const metaPropertyList = getMetadataObjectPropertyList(t, decoratorMetas, classBodyItems);
|
|
919
|
+
if (metaPropertyList.length === 0) {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
decoratorPaths.forEach((path) => path.remove());
|
|
923
|
+
const isAnonymousClassDeclaration = path.isClassDeclaration() && !path.get('id').isIdentifier();
|
|
924
|
+
const shouldTransformAsClassExpression = path.isClassExpression() || isAnonymousClassDeclaration;
|
|
925
|
+
if (shouldTransformAsClassExpression) {
|
|
926
|
+
// Example:
|
|
927
|
+
// export default class extends LightningElement {}
|
|
928
|
+
// Output:
|
|
929
|
+
// export default registerDecorators(class extends LightningElement {});
|
|
930
|
+
// if it does not have an id, we can treat it as a ClassExpression
|
|
931
|
+
const classExpression = t.toExpression(node);
|
|
932
|
+
path.replaceWith(createRegisterDecoratorsCallExpression(path, classExpression, metaPropertyList));
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
// Example: export default class NamedClass extends LightningElement {}
|
|
936
|
+
// Output:
|
|
937
|
+
// export default class NamedClass extends LightningElement {}
|
|
938
|
+
// registerDecorators(NamedClass);
|
|
939
|
+
// Note: This will be further transformed
|
|
940
|
+
const statementPath = path.getStatementParent();
|
|
941
|
+
statementPath.insertAfter(t.expressionStatement(createRegisterDecoratorsCallExpression(path, node.id, metaPropertyList)));
|
|
942
|
+
}
|
|
943
|
+
},
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/*
|
|
948
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
949
|
+
* All rights reserved.
|
|
950
|
+
* SPDX-License-Identifier: MIT
|
|
951
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
952
|
+
*/
|
|
953
|
+
function defaultImport(t, specifiers) {
|
|
954
|
+
const defaultImport = specifiers.find((s) => t.isImportDefaultSpecifier(s));
|
|
955
|
+
return defaultImport && defaultImport.local.name;
|
|
956
|
+
}
|
|
957
|
+
function dedupeImports ({ types: t }) {
|
|
958
|
+
return function (path) {
|
|
959
|
+
const body = path.get('body');
|
|
960
|
+
const importStatements = body.filter((s) => s.isImportDeclaration());
|
|
961
|
+
const visited = new Map();
|
|
962
|
+
importStatements.forEach((importPath) => {
|
|
963
|
+
const sourceLiteral = importPath.node.source;
|
|
964
|
+
// If the import is of the type import * as X, just ignore it since we can't dedupe
|
|
965
|
+
if (importPath.node.specifiers.some((_) => t.isImportNamespaceSpecifier(_))) {
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
// If we have seen the same source, we will try to dedupe it
|
|
969
|
+
if (visited.has(sourceLiteral.value)) {
|
|
970
|
+
const visitedImport = visited.get(sourceLiteral.value);
|
|
971
|
+
const visitedSpecifiers = visitedImport.node.specifiers;
|
|
972
|
+
const visitedDefaultImport = defaultImport(t, visitedSpecifiers);
|
|
973
|
+
// We merge all the named imports unless is a default with the same name
|
|
974
|
+
let canImportBeRemoved = true;
|
|
975
|
+
importPath.node.specifiers.forEach((s) => {
|
|
976
|
+
if (visitedDefaultImport && t.isImportDefaultSpecifier(s)) {
|
|
977
|
+
if (visitedDefaultImport !== s.local.name) {
|
|
978
|
+
canImportBeRemoved = false;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
visitedSpecifiers.push(s);
|
|
983
|
+
}
|
|
984
|
+
});
|
|
985
|
+
if (canImportBeRemoved) {
|
|
986
|
+
importPath.remove();
|
|
987
|
+
}
|
|
988
|
+
// We need to sort the imports due to a bug in babel where default must be first
|
|
989
|
+
visitedSpecifiers.sort((a) => (t.isImportDefaultSpecifier(a) ? -1 : 1));
|
|
990
|
+
}
|
|
991
|
+
else {
|
|
992
|
+
visited.set(sourceLiteral.value, importPath);
|
|
993
|
+
}
|
|
994
|
+
});
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function getImportSource(path) {
|
|
999
|
+
return path.parentPath.get('arguments.0');
|
|
1000
|
+
}
|
|
1001
|
+
function validateImport(sourcePath) {
|
|
1002
|
+
if (!sourcePath.isStringLiteral()) {
|
|
1003
|
+
throw generateError(sourcePath, {
|
|
1004
|
+
errorInfo: LWCClassErrors.INVALID_DYNAMIC_IMPORT_SOURCE_STRICT,
|
|
1005
|
+
messageArgs: [String(sourcePath)],
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
/*
|
|
1010
|
+
* Expected API for this plugin:
|
|
1011
|
+
* { dynamicImports: { loader: string, strictSpecifier: boolean } }
|
|
1012
|
+
*/
|
|
1013
|
+
function dynamicImports () {
|
|
1014
|
+
function getLoaderRef(path, loaderName, state) {
|
|
1015
|
+
if (!state.loaderRef) {
|
|
1016
|
+
state.loaderRef = addNamed(path, 'load', loaderName);
|
|
1017
|
+
}
|
|
1018
|
+
return state.loaderRef;
|
|
1019
|
+
}
|
|
1020
|
+
function addDynamicImportDependency(dependency, state) {
|
|
1021
|
+
// TODO [#3444]: state.dynamicImports seems unused and can probably be deleted
|
|
1022
|
+
if (!state.dynamicImports) {
|
|
1023
|
+
state.dynamicImports = [];
|
|
1024
|
+
}
|
|
1025
|
+
if (!state.dynamicImports.includes(dependency)) {
|
|
1026
|
+
state.dynamicImports.push(dependency);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return {
|
|
1030
|
+
Import(path, state) {
|
|
1031
|
+
const { dynamicImports } = state.opts;
|
|
1032
|
+
if (!dynamicImports) {
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
const { loader, strictSpecifier } = dynamicImports;
|
|
1036
|
+
const sourcePath = getImportSource(path);
|
|
1037
|
+
if (strictSpecifier) {
|
|
1038
|
+
validateImport(sourcePath);
|
|
1039
|
+
}
|
|
1040
|
+
if (loader) {
|
|
1041
|
+
const loaderId = getLoaderRef(path, loader, state);
|
|
1042
|
+
path.replaceWith(loaderId);
|
|
1043
|
+
}
|
|
1044
|
+
if (sourcePath.isStringLiteral()) {
|
|
1045
|
+
addDynamicImportDependency(sourcePath.node.value, state);
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/*
|
|
1052
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
1053
|
+
* All rights reserved.
|
|
1054
|
+
* SPDX-License-Identifier: MIT
|
|
1055
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
1056
|
+
*/
|
|
1057
|
+
function scopeCssImports ({ types: t }, path) {
|
|
1058
|
+
const programPath = path.isProgram() ? path : path.findParent((node) => node.isProgram());
|
|
1059
|
+
programPath.get('body').forEach((node) => {
|
|
1060
|
+
if (node.isImportDeclaration()) {
|
|
1061
|
+
const source = node.get('source');
|
|
1062
|
+
if (source.type === 'StringLiteral' && source.node.value.endsWith('.scoped.css')) {
|
|
1063
|
+
source.replaceWith(t.stringLiteral(source.node.value + '?scoped=true'));
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
function compilerVersionNumber({ types: t }) {
|
|
1070
|
+
return {
|
|
1071
|
+
ClassBody(path) {
|
|
1072
|
+
if (path.parent.superClass === null) {
|
|
1073
|
+
// Components *must* extend from either LightningElement or some other superclass (e.g. a mixin).
|
|
1074
|
+
// We can skip classes without a superclass to avoid adding unnecessary comments.
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
// If the class body is empty, we want an inner comment. Otherwise we want it after the last child
|
|
1078
|
+
// of the class body. In either case, we want it right before the `}` at the end of the function body.
|
|
1079
|
+
if (path.node.body.length > 0) {
|
|
1080
|
+
// E.g. `class Foo extends Lightning Element { /*LWC compiler v1.2.3*/ }`
|
|
1081
|
+
t.addComment(path.node.body[path.node.body.length - 1], 'trailing', LWC_VERSION_COMMENT,
|
|
1082
|
+
/* line */ false);
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
// E.g. `class Foo extends Lightning Element { bar = 'baz'; /*LWC compiler v1.2.3*/ }`
|
|
1086
|
+
t.addComment(path.node, 'inner', LWC_VERSION_COMMENT, /* line */ false);
|
|
1087
|
+
}
|
|
1088
|
+
},
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/*
|
|
1093
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
1094
|
+
* All rights reserved.
|
|
1095
|
+
* SPDX-License-Identifier: MIT
|
|
1096
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
1097
|
+
*/
|
|
1098
|
+
/**
|
|
1099
|
+
* The transform is done in 2 passes:
|
|
1100
|
+
* - First, apply in a single AST traversal the decorators and the component transformation.
|
|
1101
|
+
* - Then, in a second path transform class properties using the official babel plugin "babel-plugin-transform-class-properties".
|
|
1102
|
+
*/
|
|
1103
|
+
function LwcClassTransform(api) {
|
|
1104
|
+
const { ExportDefaultDeclaration: transformCreateRegisterComponent } = component(api);
|
|
1105
|
+
const { Class: transformDecorators } = decorators(api);
|
|
1106
|
+
const { Import: transformDynamicImports } = dynamicImports();
|
|
1107
|
+
const { ClassBody: addCompilerVersionNumber } = compilerVersionNumber(api);
|
|
1108
|
+
return {
|
|
1109
|
+
manipulateOptions(opts, parserOpts) {
|
|
1110
|
+
parserOpts.plugins.push('classProperties', [
|
|
1111
|
+
'decorators',
|
|
1112
|
+
{ decoratorsBeforeExport: true },
|
|
1113
|
+
]);
|
|
1114
|
+
},
|
|
1115
|
+
visitor: {
|
|
1116
|
+
// The LWC babel plugin is incompatible with other plugins. To get around this, we run the LWC babel plugin
|
|
1117
|
+
// first by running all its traversals from this Program visitor.
|
|
1118
|
+
Program: {
|
|
1119
|
+
enter(path) {
|
|
1120
|
+
const engineImportSpecifiers = getEngineImportSpecifiers(path);
|
|
1121
|
+
// Validate the usage of LWC decorators.
|
|
1122
|
+
validateImportedLwcDecoratorUsage(engineImportSpecifiers);
|
|
1123
|
+
// Add ?scoped=true to *.scoped.css imports
|
|
1124
|
+
scopeCssImports(api, path);
|
|
1125
|
+
},
|
|
1126
|
+
exit(path) {
|
|
1127
|
+
const engineImportSpecifiers = getEngineImportSpecifiers(path);
|
|
1128
|
+
removeImportedDecoratorSpecifiers(engineImportSpecifiers);
|
|
1129
|
+
// Will eventually be removed to eliminate unnecessary complexity. Rollup already does this for us.
|
|
1130
|
+
dedupeImports(api)(path);
|
|
1131
|
+
},
|
|
1132
|
+
},
|
|
1133
|
+
Import: transformDynamicImports,
|
|
1134
|
+
Class: transformDecorators,
|
|
1135
|
+
ClassBody: addCompilerVersionNumber,
|
|
1136
|
+
ExportDefaultDeclaration: transformCreateRegisterComponent,
|
|
1137
|
+
},
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
export { LwcClassTransform as default };
|
|
1142
|
+
/** version: 2.43.0 */
|
|
1143
|
+
//# sourceMappingURL=index.js.map
|