@salesforce/core 4.2.2 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -23,6 +23,10 @@ See the [API documentation](https://forcedotcom.github.io/sfdx-core/).
|
|
|
23
23
|
|
|
24
24
|
If you are interested in contributing, please take a look at the [CONTRIBUTING](CONTRIBUTING.md) guide.
|
|
25
25
|
|
|
26
|
+
## Issues
|
|
27
|
+
|
|
28
|
+
Please report all issues to [issues only repository](https://github.com/forcedotcom/cli/issues).
|
|
29
|
+
|
|
26
30
|
# Using TestSetup
|
|
27
31
|
|
|
28
32
|
The Salesforce DX Core Library provides a unit testing utility to help with mocking and sand-boxing core components. This feature allows unit tests to execute without needing to make API calls to salesforce.com.
|
|
@@ -220,3 +224,45 @@ describe('Testing log lines', () => {
|
|
|
220
224
|
});
|
|
221
225
|
});
|
|
222
226
|
```
|
|
227
|
+
|
|
228
|
+
## Message Transformer
|
|
229
|
+
|
|
230
|
+
the Messages class, by default, will load message text during run time. It's optimized to do this only per file.
|
|
231
|
+
|
|
232
|
+
If you're using @salesforce/core or other code that uses its Messages class in a bundler (webpack, esbuild, etc) it may struggle with these runtime references.
|
|
233
|
+
|
|
234
|
+
src/messageTransformer will "inline" the messages into the js files during TS compile using `https://github.com/cevek/ttypescript`.
|
|
235
|
+
|
|
236
|
+
In your plugin or library,
|
|
237
|
+
|
|
238
|
+
`yarn add --dev ttypescript`
|
|
239
|
+
|
|
240
|
+
> tsconfig.json
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
...
|
|
245
|
+
"plugins": [{ "transform": "@salesforce/core/lib/messageTransformer", "import": "messageTransformer" }]
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
> .sfdevrc.json, which gets merged into package.json
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
"wireit": {
|
|
253
|
+
"compile": {
|
|
254
|
+
"command": "ttsc -p . --pretty --incremental",
|
|
255
|
+
"files": [
|
|
256
|
+
"src/**/*.ts",
|
|
257
|
+
"tsconfig.json",
|
|
258
|
+
"messages"
|
|
259
|
+
],
|
|
260
|
+
"output": [
|
|
261
|
+
"lib/**",
|
|
262
|
+
"*.tsbuildinfo"
|
|
263
|
+
],
|
|
264
|
+
"clean": "if-file-deleted"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @experimental
|
|
5
|
+
* transforms `messages` references from dynamic run-time to static compile-time values
|
|
6
|
+
*/
|
|
7
|
+
export declare const messageTransformer: () => ts.TransformerFactory<ts.SourceFile>;
|
|
8
|
+
export default messageTransformer;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2023, salesforce.com, inc.
|
|
4
|
+
* All rights reserved.
|
|
5
|
+
* Licensed under the BSD 3-Clause license.
|
|
6
|
+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.messageTransformer = void 0;
|
|
10
|
+
const ts = require("typescript");
|
|
11
|
+
const messages_1 = require("./messages");
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @experimental
|
|
15
|
+
* transforms `messages` references from dynamic run-time to static compile-time values
|
|
16
|
+
*/
|
|
17
|
+
const messageTransformer = () => {
|
|
18
|
+
messages_1.Messages.importMessagesDirectory(process.cwd());
|
|
19
|
+
const transformerFactory = (context) => (sourceFile) => {
|
|
20
|
+
if (
|
|
21
|
+
// if there are no messages, no transformation is needed
|
|
22
|
+
!sourceFile.statements.some((i) => ts.isImportDeclaration(i) && i.importClause?.getText().includes('Messages')) ||
|
|
23
|
+
// don't transform the transformer itself
|
|
24
|
+
sourceFile.fileName.includes('messageTransformer.ts')) {
|
|
25
|
+
return sourceFile;
|
|
26
|
+
}
|
|
27
|
+
const visitor = (node) => {
|
|
28
|
+
if (ts.isExpressionStatement(node) && node.getText().includes('importMessagesDirectory')) {
|
|
29
|
+
// importMessagesDirectory now happens at compile, not in runtime
|
|
30
|
+
// returning undefined removes the node
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
if (
|
|
34
|
+
// transform a runtime load call into hardcoded messages values
|
|
35
|
+
// const foo = Messages.load|loadMessages('pluginName', 'messagesFile' ...) =>
|
|
36
|
+
// const foo = new Messages('pluginName', 'messagesFile', new Map([['key', 'value']]))
|
|
37
|
+
ts.isCallExpression(node) &&
|
|
38
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
39
|
+
node.expression.expression.getText() === 'Messages' &&
|
|
40
|
+
node.expression.name.getText().includes('load')) {
|
|
41
|
+
// we always want the first two arguments, which are the plugin name and the messages file name
|
|
42
|
+
const arrayMembers = node.arguments.slice(0, 2);
|
|
43
|
+
const arrayMembersText = arrayMembers.map(getTextWithoutQuotes);
|
|
44
|
+
// Messages doesn't care whether you call messages.load or loadMessages, it loads the whole file
|
|
45
|
+
const messagesInstance = messages_1.Messages.loadMessages(arrayMembersText[0], arrayMembersText[1]);
|
|
46
|
+
return context.factory.createNewExpression(node.expression.expression, undefined, [
|
|
47
|
+
arrayMembers[0],
|
|
48
|
+
arrayMembers[1],
|
|
49
|
+
context.factory.createNewExpression(context.factory.createIdentifier('Map'), undefined, [
|
|
50
|
+
messageMapToHardcodedMap(messagesInstance.messages),
|
|
51
|
+
]),
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
// it might be a node that contains one of the things we're interested in, so keep digging
|
|
55
|
+
return ts.visitEachChild(node, visitor, context);
|
|
56
|
+
};
|
|
57
|
+
return ts.visitNode(sourceFile, visitor);
|
|
58
|
+
};
|
|
59
|
+
return transformerFactory;
|
|
60
|
+
};
|
|
61
|
+
exports.messageTransformer = messageTransformer;
|
|
62
|
+
exports.default = exports.messageTransformer;
|
|
63
|
+
const getTextWithoutQuotes = (node) => node.getText().replace(/'/g, '');
|
|
64
|
+
/** turn a loaded message map into */
|
|
65
|
+
const messageMapToHardcodedMap = (messages) => ts.factory.createArrayLiteralExpression(Array.from(messages).map(([key, value]) => {
|
|
66
|
+
// case 1: string
|
|
67
|
+
if (typeof value === 'string') {
|
|
68
|
+
return ts.factory.createArrayLiteralExpression([
|
|
69
|
+
ts.factory.createStringLiteral(key),
|
|
70
|
+
ts.factory.createStringLiteral(value),
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
else if (Array.isArray(value)) {
|
|
74
|
+
// case 2: string[]
|
|
75
|
+
return ts.factory.createArrayLiteralExpression([
|
|
76
|
+
ts.factory.createStringLiteral(key),
|
|
77
|
+
ts.factory.createArrayLiteralExpression(value.map((v) => ts.factory.createStringLiteral(v))),
|
|
78
|
+
]);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// turn the object into a map and recurse!
|
|
82
|
+
return messageMapToHardcodedMap(new Map(Object.entries(value)));
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
//# sourceMappingURL=messageTransformer.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.1",
|
|
4
4
|
"description": "Core libraries to interact with SFDX projects, orgs, and APIs.",
|
|
5
5
|
"main": "lib/exported",
|
|
6
6
|
"types": "lib/exported.d.ts",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@types/proper-lockfile": "^4.1.2",
|
|
68
68
|
"@types/shelljs": "0.8.12",
|
|
69
69
|
"@typescript-eslint/eslint-plugin": "^5.59.9",
|
|
70
|
-
"@typescript-eslint/parser": "^5.59.
|
|
70
|
+
"@typescript-eslint/parser": "^5.59.9",
|
|
71
71
|
"chai": "^4.3.7",
|
|
72
72
|
"chai-string": "^1.5.0",
|
|
73
73
|
"eslint": "^8.41.0",
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2023, salesforce.com, inc.
|
|
3
|
-
* All rights reserved.
|
|
4
|
-
* Licensed under the BSD 3-Clause license.
|
|
5
|
-
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
-
*/
|
|
7
|
-
/* eslint-disable no-console */
|
|
8
|
-
/* eslint-disable complexity */
|
|
9
|
-
|
|
10
|
-
import * as ts from 'typescript';
|
|
11
|
-
import { Messages, StoredMessageMap } from '../src/messages';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
15
|
-
* @experimental
|
|
16
|
-
* transforms `messages` references from dynamic run-time to static compile-time values
|
|
17
|
-
*/
|
|
18
|
-
const transformer = (program: ts.Program, pluginOptions: {}) => {
|
|
19
|
-
Messages.importMessagesDirectory(process.cwd());
|
|
20
|
-
const transformerFactory: ts.TransformerFactory<ts.SourceFile> = (context) => {
|
|
21
|
-
return (sourceFile) => {
|
|
22
|
-
// if there are no messages, no transformation is needed
|
|
23
|
-
if (
|
|
24
|
-
!sourceFile.statements.some((i) => ts.isImportDeclaration(i) && i.importClause?.getText().includes('Messages'))
|
|
25
|
-
) {
|
|
26
|
-
return sourceFile;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const visitor = (node: ts.Node): ts.VisitResult<ts.Node> => {
|
|
30
|
-
if (ts.isExpressionStatement(node) && node.getText().includes('importMessagesDirectory')) {
|
|
31
|
-
// importMessagesDirectory now happens at compile, not in runtime
|
|
32
|
-
// returning undefined removes the node
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
if (
|
|
36
|
-
// transform a runtime load call into hardcoded messages values
|
|
37
|
-
// const foo = Messages.load|loadMessages('pluginName', 'messagesFile' ...) =>
|
|
38
|
-
// const foo = new Messages('pluginName', 'messagesFile', new Map([['key', 'value']]))
|
|
39
|
-
ts.isCallExpression(node) &&
|
|
40
|
-
ts.isPropertyAccessExpression(node.expression) &&
|
|
41
|
-
node.expression.expression.getText() === 'Messages' &&
|
|
42
|
-
node.expression.name.getText().includes('load')
|
|
43
|
-
) {
|
|
44
|
-
// we always want the first two arguments, which are the plugin name and the messages file name
|
|
45
|
-
const arrayMembers = node.arguments.slice(0, 2);
|
|
46
|
-
const arrayMembersText = arrayMembers.map(getTextWithoutQuotes);
|
|
47
|
-
|
|
48
|
-
// Messages doesn't care whether you call messages.load or loadMessages, it loads the whole file
|
|
49
|
-
const messagesInstance = Messages.loadMessages(arrayMembersText[0], arrayMembersText[1]);
|
|
50
|
-
return context.factory.createNewExpression(node.expression.expression, undefined, [
|
|
51
|
-
arrayMembers[0],
|
|
52
|
-
arrayMembers[1],
|
|
53
|
-
context.factory.createNewExpression(context.factory.createIdentifier('Map'), undefined, [
|
|
54
|
-
messageMapToHardcodedMap(messagesInstance.messages),
|
|
55
|
-
]),
|
|
56
|
-
]);
|
|
57
|
-
}
|
|
58
|
-
// it might be a node that contains one of the things we're interested in, so keep digging
|
|
59
|
-
return ts.visitEachChild(node, visitor, context);
|
|
60
|
-
};
|
|
61
|
-
return ts.visitNode(sourceFile, visitor);
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
return transformerFactory;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export default transformer;
|
|
68
|
-
|
|
69
|
-
const getTextWithoutQuotes = (node: ts.Node): string => node.getText().replace(/'/g, '');
|
|
70
|
-
|
|
71
|
-
/** turn a loaded message map into */
|
|
72
|
-
const messageMapToHardcodedMap = (messages: StoredMessageMap): ts.ArrayLiteralExpression => {
|
|
73
|
-
return ts.factory.createArrayLiteralExpression(
|
|
74
|
-
Array.from(messages).map(([key, value]) => {
|
|
75
|
-
// case 1: string
|
|
76
|
-
if (typeof value === 'string') {
|
|
77
|
-
return ts.factory.createArrayLiteralExpression([
|
|
78
|
-
ts.factory.createStringLiteral(key),
|
|
79
|
-
ts.factory.createStringLiteral(value),
|
|
80
|
-
]);
|
|
81
|
-
} else if (Array.isArray(value)) {
|
|
82
|
-
// case 2: string[]
|
|
83
|
-
return ts.factory.createArrayLiteralExpression([
|
|
84
|
-
ts.factory.createStringLiteral(key),
|
|
85
|
-
ts.factory.createArrayLiteralExpression(value.map((v) => ts.factory.createStringLiteral(v))),
|
|
86
|
-
]);
|
|
87
|
-
} else {
|
|
88
|
-
// turn the object into a map and recurse!
|
|
89
|
-
return messageMapToHardcodedMap(new Map(Object.entries(value)));
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
);
|
|
93
|
-
};
|