@lwc/sfdc-compiler-utils 2.19.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.
@@ -0,0 +1,366 @@
1
+ // Copyright (c) 2022, Salesforce, Inc.,
2
+ // All rights reserved.
3
+ // For full license text, see the LICENSE.txt file
4
+
5
+ const SALESFORCE_IMPORT_PREFIX = '@salesforce/';
6
+ const DEFAULT_SALESFORCE_NAMESPACE = 'c';
7
+
8
+ /**
9
+ * @param: moduleName - 'c.label'
10
+ * @param: moduleType - 'label'
11
+ * @param: namespaceMapping<sting, string> - { c: 'ns' }
12
+ *
13
+ * @return: string - 'label/ns.label'
14
+ */
15
+ export function getNamespacedIdForType(
16
+ moduleName: string,
17
+ moduleType: string | undefined,
18
+ namespaceMapping: { [name: string]: string }
19
+ ): string | undefined {
20
+ validateParameters(moduleName, namespaceMapping);
21
+
22
+ let updatedModuleName;
23
+
24
+ if (moduleType && moduleType !== 'module') {
25
+ updatedModuleName = getSalesforceNamespacedModule(moduleName, moduleType, namespaceMapping);
26
+ } else {
27
+ updatedModuleName = getStandardNamespacedModule(moduleName, namespaceMapping);
28
+ }
29
+ return updatedModuleName;
30
+ }
31
+
32
+ /**
33
+ * Retrieve and alias @salesforce scoped resource id. If provided resource doesn't start
34
+ * with @salesforce prefix, the resource is treated as a regular module
35
+ *
36
+ * @param: moduleName - '@salesforce/label/c.label'
37
+ * @param: namespaceMapping<sting, string> - { c: 'ns' }
38
+ *
39
+ * @return: string - 'ns.label'
40
+ */
41
+ export function getNamespacedIdForResource(
42
+ moduleName: string,
43
+ namespaceMapping: { [name: string]: string }
44
+ ): undefined | string {
45
+ validateParameters(moduleName, namespaceMapping);
46
+
47
+ let updatedModuleName;
48
+ if (moduleName.startsWith(SALESFORCE_IMPORT_PREFIX)) {
49
+ const [prefix, type, value] = moduleName.split('/');
50
+ const updatedModuleId = getSalesforceNamespacedModule(value, type, namespaceMapping);
51
+ if (updatedModuleId) {
52
+ updatedModuleName = [prefix, type, updatedModuleId].join('/');
53
+ }
54
+ } else {
55
+ const updateModuleId = getStandardNamespacedModule(moduleName, namespaceMapping);
56
+ if (updateModuleId) {
57
+ updatedModuleName = updateModuleId;
58
+ }
59
+ }
60
+
61
+ return updatedModuleName;
62
+ }
63
+
64
+ function getSalesforceNamespacedModule(
65
+ value: string,
66
+ type: string,
67
+ namespaceMapping: { [name: string]: string }
68
+ ): undefined | string {
69
+ if (!value) {
70
+ return undefined;
71
+ }
72
+
73
+ let existingNamespace: string | undefined;
74
+ let newNamespace: string | undefined;
75
+ let parts;
76
+
77
+ const defaultTargetValue = namespaceMapping[DEFAULT_SALESFORCE_NAMESPACE];
78
+
79
+ switch (type) {
80
+ // @salesforce/label/c.label1 -> @salesforce/label/namespace.label1
81
+ case 'label':
82
+ parts = value.split('.');
83
+
84
+ // filter out invalid format:
85
+ // @salesforce/label/c..label
86
+ if (parts.some((p) => !p)) {
87
+ return undefined;
88
+ }
89
+
90
+ existingNamespace = parts[0];
91
+
92
+ // no namespace - @salesforce/label/label1
93
+ if (parts.length === 1) {
94
+ return undefined;
95
+ }
96
+ newNamespace = namespaceMapping[existingNamespace];
97
+
98
+ return newNamespace
99
+ ? value.replace(`${existingNamespace}.`, `${newNamespace}.`)
100
+ : value;
101
+
102
+ // @salesforce/resourceUrl/resource1 -> @salesforce/resourceUrl/namespace__resource1
103
+ // @salesforce/contentAssetUrl/resource1 -> @salesforce/contentAssetUrl/namespace__resource1
104
+ // @salesforce/customPermission/resource1 -> @salesforce/customPermission/namespace__resource1
105
+ // @salesforce/komaci/resource1 -> @salesforce/komaci/namespace__resource1
106
+ case 'resourceUrl':
107
+ case 'contentAssetUrl':
108
+ case 'customPermission':
109
+ case 'komaci':
110
+ parts = value.split('__');
111
+
112
+ // filter out invalid format:
113
+ if (parts.some((p) => !p)) {
114
+ return undefined;
115
+ }
116
+
117
+ if (parts.length === 1) {
118
+ return defaultTargetValue && `${defaultTargetValue}__${value}`;
119
+ }
120
+
121
+ existingNamespace = parts[0];
122
+
123
+ // @salesforce/resourceUrl/namespace__resource1
124
+ newNamespace = namespaceMapping[existingNamespace];
125
+
126
+ return newNamespace
127
+ ? value.replace(`${existingNamespace}__`, `${newNamespace}__`)
128
+ : value;
129
+
130
+ // Standard messageChannels should have a namespace, e.g. @salesforce/messageChannel/standardNs_messageChannel1
131
+ // Undefined behavior for a missing "__c" suffix (indicating custom) and a missing namespace
132
+ // @salesforce/messageChannel/messageChannel1 -> undefined
133
+ // @salesforce/messageChannel/messageChannel1__c -> @salesforce/messageChannel/namespace__messageChannel1__c
134
+ case 'messageChannel':
135
+ parts = value.split('__');
136
+
137
+ // filter out invalid format: @salesforce/messageChannel/messageChannel1
138
+ if (parts.length === 1 || parts.some((p) => !p)) {
139
+ return undefined;
140
+ }
141
+
142
+ if (value.endsWith('__c') && parts.length === 2) {
143
+ return defaultTargetValue && `${defaultTargetValue}__${value}`;
144
+ }
145
+
146
+ existingNamespace = parts[0];
147
+
148
+ // @salesforce/messageChannel/namespace__messageChannel1
149
+ newNamespace = namespaceMapping[existingNamespace];
150
+
151
+ return newNamespace
152
+ ? value.replace(`${existingNamespace}__`, `${newNamespace}__`)
153
+ : value;
154
+ // apexContinuation is the same as apex
155
+ // @salesforce/apexContinuation/MyClass.methodA -> @salesforce/apexContinuation/acme.MyClass.methodA
156
+ // @salesforce/apex/MyClass.methodA -> @salesforce/apex/acme.MyClass.methodA
157
+ case 'apexContinuation':
158
+ case 'apex':
159
+ // We detect if the apex reference already contains the namespace by checking the number of parts in its
160
+ // reference. If the namespace is present the value would have the following from <ns>.<class>.<method>
161
+ // otherwise it would be <class>.<method>.
162
+
163
+ parts = value.split('.');
164
+
165
+ // filter out invalid format:
166
+ // @salesforce/apex/MyClass
167
+ // @salesforce/apex/MyClass..methodA
168
+ if (parts.length === 1 || parts.some((p) => !p)) {
169
+ return undefined;
170
+ }
171
+
172
+ // @salesforce/apex/MyClass.method -> missing namespace
173
+ if (parts.length === 2) {
174
+ return defaultTargetValue && `${defaultTargetValue}.${value}`;
175
+ }
176
+
177
+ existingNamespace = parts[0];
178
+ newNamespace = namespaceMapping[existingNamespace];
179
+ return newNamespace
180
+ ? value.replace(`${existingNamespace}.`, `${newNamespace}.`)
181
+ : value;
182
+
183
+ // @salesforce/schema/CustomObject1__c -> @salesforce/schema/acme__CustomObject1__c
184
+ // @salesforce/schema/Account.CustomField__c -> @salesforce/schema/Account.acme__CustomField__c
185
+ // @salesforce/schema/Account.Relationship__r.CustomField__c ->
186
+ // -> @salesforce/schema/Account.acme__Relationship__r.acme__CustomField__c
187
+ case 'schema': {
188
+ // For each object, field and relationship that is part of the module name, if it is custom and has no
189
+ // namespace, we need to add the target namespace.
190
+ const schemaParts = value.split('.');
191
+ const remappedParts = schemaParts.map((part) => {
192
+ const isCustom = part.endsWith('__c') || part.endsWith('__r');
193
+ if (!isCustom) {
194
+ return part;
195
+ }
196
+
197
+ parts = part.split('__');
198
+
199
+ // @salesforce/schema/c____CustomObject1
200
+ if (parts.some((p) => !p)) {
201
+ return undefined;
202
+ }
203
+
204
+ const isNamespaced = parts.length > 2;
205
+
206
+ // @salesforce/schema/CustomObject1__c
207
+ if (!isNamespaced) {
208
+ return defaultTargetValue && `${defaultTargetValue}__${part}`;
209
+ }
210
+
211
+ // @salesforce/schema/acme__CustomObject1__c
212
+ existingNamespace = parts[0];
213
+ newNamespace = namespaceMapping[existingNamespace];
214
+
215
+ // replace only if new mapping exists. ex: acme -> newAcme
216
+ return newNamespace
217
+ ? part.replace(`${existingNamespace}__`, `${newNamespace}__`)
218
+ : part;
219
+ });
220
+
221
+ // return undefined if any of the parts were not successfully remapped
222
+ return remappedParts.some((p) => !p) ? undefined : remappedParts.join('.');
223
+ }
224
+
225
+ // noop
226
+ case 'accessCheck':
227
+ case 'client':
228
+ case 'commerce':
229
+ case 'community':
230
+ case 'cssvars':
231
+ case 'gate':
232
+ case 'i18n':
233
+ case 'internal':
234
+ case 'metric':
235
+ case 'site':
236
+ case 'slds':
237
+ case 'user':
238
+ case 'userPermission':
239
+ return undefined;
240
+ default:
241
+ throw new Error(
242
+ `Failed to apply namespace mapping to "${value}" due to unknown @salesforce type ${type}.`
243
+ );
244
+ }
245
+ }
246
+
247
+ function getStandardNamespacedModule(
248
+ moduleName: string,
249
+ namespaceMapping: { [name: string]: string }
250
+ ): undefined | string {
251
+ // module format expected to be in the following format: c/utils
252
+ const parts = moduleName.split('/');
253
+
254
+ // assert no empty values
255
+ if (parts.some((p) => !p)) {
256
+ return undefined;
257
+ }
258
+
259
+ const original = parts[0];
260
+
261
+ const target = namespaceMapping[original];
262
+ return target ? moduleName.replace(`${original}/`, `${target}/`) : undefined;
263
+ }
264
+
265
+ function validateParameters(
266
+ moduleName: string,
267
+ namespaceMapping: { [name: string]: string }
268
+ ): void {
269
+ if (!moduleName) {
270
+ throw new Error(
271
+ `Failed to apply namespace mapping to "${moduleName}". Missing required "moduleName" parameter`
272
+ );
273
+ }
274
+
275
+ if (!namespaceMapping) {
276
+ throw new Error(
277
+ `Failed to apply namespace mapping to "${moduleName}". Missing required "namespaceMapping" parameter`
278
+ );
279
+ }
280
+ }
281
+
282
+ export function containsExplicitNamespace(
283
+ value: string,
284
+ type: string,
285
+ explicitNamespace: string
286
+ ): boolean {
287
+ if (!value || !type || !explicitNamespace) {
288
+ throw new Error(
289
+ `Failed to check explicit namespace usage.` +
290
+ `Expected string values for "value", "type", and "explicitNamespace" parameters, ` +
291
+ `received "${value}," "${type}", "${explicitNamespace}".`
292
+ );
293
+ }
294
+
295
+ // nothing to match when default namespace is supplied
296
+ if (explicitNamespace === DEFAULT_SALESFORCE_NAMESPACE) {
297
+ return false;
298
+ }
299
+
300
+ let isExplicitNamespaceUsed = false;
301
+
302
+ switch (type) {
303
+ case 'component':
304
+ // lowercase incoming namespace value because template references are always lowercased
305
+ isExplicitNamespaceUsed = value.startsWith(`${explicitNamespace.toLocaleLowerCase()}-`);
306
+ break;
307
+
308
+ case 'module':
309
+ isExplicitNamespaceUsed = value.startsWith(`${explicitNamespace}/`);
310
+ break;
311
+
312
+ // @salesforce/label/c.label1 -> @salesforce/label/namespace.label1
313
+ case 'label':
314
+ isExplicitNamespaceUsed = value.startsWith(`${explicitNamespace}.`);
315
+ break;
316
+
317
+ // @salesforce/schema/Account.CustomField__c -> @salesforce/schema/Account.acme__CustomField__c
318
+ // @salesforce/schema/Account.acme__Relationship__r.acme__CustomField__c
319
+ // @salesforce/resourceUrl/resource1 -> @salesforce/resourceUrl/namespace__resource1
320
+ // @salesforce/contentAssetUrl/asset -> @salesforce/contentAssetUrl/nsC__asset;
321
+ // @salesforce/customPermission/perm -> @salesforce/customPermission/namespace__perm;
322
+ // @salesforce/komaci/resource1 -> @salesforce/komaci/namespace__resource1
323
+ // @salesforce/messageChannel/messageChannel__c -> @salesforce/messageChannel/namespace__messageChannel__c;
324
+ case 'schema':
325
+ case 'resourceUrl':
326
+ case 'contentAssetUrl':
327
+ case 'customPermission':
328
+ case 'komaci':
329
+ case 'messageChannel':
330
+ isExplicitNamespaceUsed = value.includes(`${explicitNamespace}__`);
331
+ break;
332
+
333
+ // @salesforce/apexContinuation/MyClass.methodA -> @salesforce/apexContinuation/acme.MyClass.methodA
334
+ // @salesforce/apex/MyClass.methodA -> @salesforce/apex/acme.MyClass.methodA
335
+ // The original resource type is 'apex', however, when we collect the reference
336
+ // we always mark it as an apexMethod.
337
+ case 'apexContinuation':
338
+ case 'apexMethod': {
339
+ // If the namespace is present the value would have the following form <ns>.<class>.<method>
340
+ // otherwise it would be <class>.<method>.
341
+ const parts = value.split('.');
342
+ if (parts.length === 3) {
343
+ isExplicitNamespaceUsed = parts[0] === explicitNamespace;
344
+ }
345
+ break;
346
+ }
347
+
348
+ // following resources are never namespaced
349
+ case 'client':
350
+ case 'cssvars':
351
+ case 'gate':
352
+ case 'i18n':
353
+ case 'metric':
354
+ case 'user':
355
+ case 'commerce':
356
+ case 'community':
357
+ case 'site':
358
+ case 'internal':
359
+ case 'userPermission':
360
+ break;
361
+ default:
362
+ throw new TypeError(`Unsupported reference type ${type}.`);
363
+ }
364
+
365
+ return isExplicitNamespaceUsed;
366
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/"]
8
+ }