@tolgee/cli 1.0.0-prerelease.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.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +34 -0
  3. package/dist/client/errors.js +36 -0
  4. package/dist/client/export.js +23 -0
  5. package/dist/client/import.js +61 -0
  6. package/dist/client/index.js +79 -0
  7. package/dist/client/internal/requester.js +145 -0
  8. package/dist/client/internal/schema.generated.js +6 -0
  9. package/dist/client/internal/schema.utils.js +2 -0
  10. package/dist/client/languages.js +16 -0
  11. package/dist/client/project.js +44 -0
  12. package/dist/commands/extract/check.js +41 -0
  13. package/dist/commands/extract/print.js +51 -0
  14. package/dist/commands/extract.js +14 -0
  15. package/dist/commands/login.js +49 -0
  16. package/dist/commands/pull.js +38 -0
  17. package/dist/commands/push.js +122 -0
  18. package/dist/commands/sync/compare.js +48 -0
  19. package/dist/commands/sync/sync.js +110 -0
  20. package/dist/commands/sync/syncUtils.js +64 -0
  21. package/dist/config/credentials.js +125 -0
  22. package/dist/config/tolgeerc.js +64 -0
  23. package/dist/constants.js +18 -0
  24. package/dist/extractor/index.js +2 -0
  25. package/dist/extractor/machines/react.js +728 -0
  26. package/dist/extractor/machines/shared/comments.js +82 -0
  27. package/dist/extractor/machines/shared/properties.js +226 -0
  28. package/dist/extractor/presets/react.js +29 -0
  29. package/dist/extractor/runner.js +39 -0
  30. package/dist/extractor/tokenizer.js +102 -0
  31. package/dist/extractor/warnings.js +89 -0
  32. package/dist/extractor/worker.js +82 -0
  33. package/dist/index.js +151 -0
  34. package/dist/options.js +37 -0
  35. package/dist/utils/ask.js +34 -0
  36. package/dist/utils/configPath.js +17 -0
  37. package/dist/utils/deferred.js +11 -0
  38. package/dist/utils/logger.js +93 -0
  39. package/dist/utils/moduleLoader.js +43 -0
  40. package/dist/utils/overwriteDir.js +38 -0
  41. package/dist/utils/zip.js +83 -0
  42. package/extractor.d.ts +21 -0
  43. package/package.json +98 -0
  44. package/textmate/THIRD_PARTY_NOTICE +31 -0
  45. package/textmate/TypeScript.tmLanguage +9728 -0
  46. package/textmate/TypeScriptReact.tmLanguage +10158 -0
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const json5_1 = require("json5");
4
+ function isValidKeyOverride(data) {
5
+ if (!('key' in data)) {
6
+ return false;
7
+ }
8
+ if (typeof data.key !== 'string') {
9
+ return false;
10
+ }
11
+ if ('ns' in data && typeof data.ns !== 'string') {
12
+ return false;
13
+ }
14
+ if ('defaultValue' in data && typeof data.defaultValue !== 'string') {
15
+ return false;
16
+ }
17
+ return true;
18
+ }
19
+ // This service is responsible for emitting events when magic comments are encountered
20
+ function default_1(callback, onReceive) {
21
+ onReceive((evt) => {
22
+ const comment = evt.data.trim();
23
+ if (comment.startsWith('@tolgee-ignore')) {
24
+ return callback({
25
+ type: 'MAGIC_COMMENT',
26
+ kind: 'ignore',
27
+ line: evt.line,
28
+ });
29
+ }
30
+ if (comment.startsWith('@tolgee-key')) {
31
+ const data = comment.slice(11).trim();
32
+ // Data is escaped; extract all as string
33
+ if (data.startsWith('\\')) {
34
+ return callback({
35
+ type: 'MAGIC_COMMENT',
36
+ kind: 'key',
37
+ keyName: data.slice(1),
38
+ line: evt.line,
39
+ });
40
+ }
41
+ // Data is a json5 struct
42
+ if (data.startsWith('{')) {
43
+ try {
44
+ const key = (0, json5_1.parse)(data);
45
+ if (!isValidKeyOverride(key)) {
46
+ // No key in the struct; invalid override
47
+ callback({
48
+ type: 'WARNING',
49
+ kind: 'W_INVALID_KEY_OVERRIDE',
50
+ line: evt.line,
51
+ });
52
+ }
53
+ else {
54
+ callback({
55
+ type: 'MAGIC_COMMENT',
56
+ kind: 'key',
57
+ keyName: key.key,
58
+ namespace: key.ns,
59
+ defaultValue: key.defaultValue,
60
+ line: evt.line,
61
+ });
62
+ }
63
+ }
64
+ catch {
65
+ callback({
66
+ type: 'WARNING',
67
+ kind: 'W_MALFORMED_KEY_OVERRIDE',
68
+ line: evt.line,
69
+ });
70
+ }
71
+ return;
72
+ }
73
+ callback({
74
+ type: 'MAGIC_COMMENT',
75
+ kind: 'key',
76
+ keyName: data,
77
+ line: evt.line,
78
+ });
79
+ }
80
+ });
81
+ }
82
+ exports.default = default_1;
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const xstate_1 = require("xstate");
4
+ // This state machine is responsible for extracting translation key properties from an object/props
5
+ exports.default = (0, xstate_1.createMachine)({
6
+ predictableActionArguments: true,
7
+ id: 'properties',
8
+ initial: 'idle',
9
+ context: {
10
+ property: null,
11
+ depth: 0,
12
+ static: false,
13
+ keyName: null,
14
+ defaultValue: null,
15
+ namespace: null,
16
+ },
17
+ states: {
18
+ idle: {
19
+ on: {
20
+ // JS/TS
21
+ 'meta.brace.square.ts': {
22
+ target: 'complex_key',
23
+ cond: 'isOpenSquare',
24
+ },
25
+ 'meta.object-literal.key.ts': {
26
+ actions: 'storePropertyType',
27
+ cond: 'isRootLevel',
28
+ },
29
+ 'punctuation.separator.key-value.ts': {
30
+ target: 'value',
31
+ cond: 'isRootLevel',
32
+ },
33
+ 'variable.other.readwrite.ts': {
34
+ actions: 'markImmediatePropertyAsDynamic',
35
+ cond: (_ctx, evt) => ['key', 'keyName', 'ns', 'defaultValue'].includes(evt.token),
36
+ },
37
+ // JSX/TSX
38
+ 'entity.other.attribute-name.tsx': {
39
+ actions: ['markPropertyAsDynamic', 'storePropertyType'],
40
+ },
41
+ 'keyword.operator.assignment.ts': {
42
+ target: 'value',
43
+ actions: 'markAsStatic',
44
+ },
45
+ },
46
+ },
47
+ complex_key: {
48
+ on: {
49
+ // On string
50
+ 'punctuation.definition.string.begin.ts': 'complex_key_string',
51
+ 'punctuation.definition.string.template.begin.ts': 'complex_key_string',
52
+ // Key end
53
+ 'meta.brace.square.ts': {
54
+ target: 'idle',
55
+ cond: 'isCloseSquare',
56
+ },
57
+ },
58
+ },
59
+ complex_key_string: {
60
+ on: {
61
+ '*': {
62
+ target: 'idle',
63
+ actions: 'storePropertyType',
64
+ },
65
+ },
66
+ },
67
+ value: {
68
+ on: {
69
+ // Extract strings
70
+ 'punctuation.definition.string.begin.ts': 'value_string',
71
+ 'punctuation.definition.string.template.begin.ts': 'value_string',
72
+ // Variable
73
+ 'variable.other.readwrite.ts': {
74
+ target: 'idle',
75
+ actions: [
76
+ 'unmarkAsStatic',
77
+ 'markPropertyAsDynamic',
78
+ 'clearPropertyType',
79
+ ],
80
+ },
81
+ // JSX Expression
82
+ 'punctuation.section.embedded.begin.tsx': {
83
+ actions: 'unmarkAsStatic',
84
+ },
85
+ // Value end
86
+ 'punctuation.separator.comma.ts': {
87
+ target: 'idle',
88
+ actions: [
89
+ 'unmarkAsStatic',
90
+ 'markPropertyAsDynamic',
91
+ 'clearPropertyType',
92
+ ],
93
+ },
94
+ 'punctuation.definition.block.ts': {
95
+ target: 'idle',
96
+ // Replay the event to let depth update itself if necessary
97
+ actions: [
98
+ 'unmarkAsStatic',
99
+ 'markPropertyAsDynamic',
100
+ 'clearPropertyType',
101
+ (0, xstate_1.send)((_ctx, evt) => evt),
102
+ ],
103
+ },
104
+ },
105
+ },
106
+ value_string: {
107
+ on: {
108
+ '*': [
109
+ {
110
+ target: 'idle',
111
+ actions: [
112
+ 'storePropertyValue',
113
+ 'clearPropertyType',
114
+ 'unmarkAsStatic',
115
+ ],
116
+ cond: (ctx) => ctx.static,
117
+ },
118
+ {
119
+ target: 'string_end',
120
+ actions: 'storePropertyValue',
121
+ },
122
+ ],
123
+ },
124
+ },
125
+ string_end: {
126
+ on: {
127
+ 'punctuation.separator.comma.ts': {
128
+ target: 'idle',
129
+ actions: 'clearPropertyType',
130
+ },
131
+ 'punctuation.definition.template-expression.begin.ts': {
132
+ target: 'idle',
133
+ actions: ['markPropertyAsDynamic', 'clearPropertyType'],
134
+ },
135
+ 'keyword.operator.arithmetic.ts': {
136
+ target: 'idle',
137
+ actions: ['markPropertyAsDynamic', 'clearPropertyType'],
138
+ },
139
+ // JSX
140
+ 'punctuation.section.embedded.end.tsx': {
141
+ target: 'idle',
142
+ actions: 'clearPropertyType',
143
+ },
144
+ },
145
+ },
146
+ end: {
147
+ type: 'final',
148
+ data: (ctx, evt) => ({
149
+ keyName: ctx.keyName,
150
+ defaultValue: ctx.defaultValue,
151
+ namespace: ctx.namespace,
152
+ lastEvent: evt,
153
+ }),
154
+ },
155
+ },
156
+ on: {
157
+ 'punctuation.definition.block.ts': [
158
+ {
159
+ actions: 'incrementDepth',
160
+ cond: 'isOpenCurly',
161
+ },
162
+ {
163
+ target: 'end',
164
+ cond: 'isFinalCloseCurly',
165
+ },
166
+ {
167
+ actions: 'decrementDepth',
168
+ cond: 'isCloseCurly',
169
+ },
170
+ ],
171
+ 'punctuation.definition.tag.end.tsx': {
172
+ target: 'end',
173
+ actions: 'markPropertyAsDynamic',
174
+ },
175
+ },
176
+ }, {
177
+ guards: {
178
+ isOpenCurly: (_ctx, evt) => evt.token === '{' &&
179
+ !evt.scopes.includes('meta.embedded.expression.tsx'),
180
+ isCloseCurly: (_ctx, evt) => evt.token === '}' &&
181
+ !evt.scopes.includes('meta.embedded.expression.tsx'),
182
+ isFinalCloseCurly: (ctx, evt) => evt.token === '}' && ctx.depth === 1,
183
+ isOpenSquare: (_ctx, evt) => evt.token === '[',
184
+ isCloseSquare: (_ctx, evt) => evt.token === ']',
185
+ isRootLevel: (ctx) => ctx.depth === 1,
186
+ },
187
+ actions: {
188
+ storePropertyType: (0, xstate_1.assign)({
189
+ property: (_ctx, evt) => evt.token,
190
+ }),
191
+ clearPropertyType: (0, xstate_1.assign)({
192
+ property: (_ctx, _evt) => null,
193
+ }),
194
+ storePropertyValue: (0, xstate_1.assign)({
195
+ keyName: (ctx, evt) => ctx.property === 'key' || ctx.property === 'keyName'
196
+ ? evt.token
197
+ : ctx.keyName,
198
+ defaultValue: (ctx, evt) => ctx.property === 'defaultValue' ? evt.token : ctx.defaultValue,
199
+ namespace: (ctx, evt) => ctx.property === 'ns' ? evt.token : ctx.namespace,
200
+ }),
201
+ markPropertyAsDynamic: (0, xstate_1.assign)({
202
+ keyName: (ctx, _evt) => ctx.property === 'key' || ctx.property === 'keyName'
203
+ ? false
204
+ : ctx.keyName,
205
+ defaultValue: (ctx, _evt) => ctx.property === 'defaultValue' ? false : ctx.defaultValue,
206
+ namespace: (ctx, _evt) => ctx.property === 'ns' ? false : ctx.namespace,
207
+ }),
208
+ markImmediatePropertyAsDynamic: (0, xstate_1.assign)({
209
+ keyName: (ctx, evt) => evt.token === 'key' || evt.token === 'keyName' ? false : ctx.keyName,
210
+ defaultValue: (ctx, evt) => evt.token === 'defaultValue' ? false : ctx.defaultValue,
211
+ namespace: (ctx, evt) => (evt.token === 'ns' ? false : ctx.namespace),
212
+ }),
213
+ incrementDepth: (0, xstate_1.assign)({
214
+ depth: (ctx, _evt) => ctx.depth + 1,
215
+ }),
216
+ decrementDepth: (0, xstate_1.assign)({
217
+ depth: (ctx, _evt) => ctx.depth - 1,
218
+ }),
219
+ markAsStatic: (0, xstate_1.assign)({
220
+ static: (_ctx, _evt) => true,
221
+ }),
222
+ unmarkAsStatic: (0, xstate_1.assign)({
223
+ static: (_ctx, _evt) => false,
224
+ }),
225
+ },
226
+ });
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const path_1 = require("path");
7
+ const xstate_1 = require("xstate");
8
+ const react_1 = __importDefault(require("../machines/react"));
9
+ const tokenizer_1 = __importDefault(require("../tokenizer"));
10
+ const EXTS = ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.jsx', '.tsx'];
11
+ const TOLGEE_REACT_PKG_RE = /["'`]@tolgee\/react["'`]|@tolgee-key/;
12
+ async function default_1(code, fileName) {
13
+ if (!EXTS.includes((0, path_1.extname)(fileName)) || !TOLGEE_REACT_PKG_RE.test(code)) {
14
+ // File is not a file that contains keys
15
+ return { warnings: [], keys: [] };
16
+ }
17
+ const tokens = await (0, tokenizer_1.default)(code, fileName);
18
+ const machine = (0, xstate_1.interpret)(react_1.default);
19
+ machine.start();
20
+ for (const token of tokens) {
21
+ machine.send(token);
22
+ }
23
+ const snapshot = machine.getSnapshot();
24
+ return {
25
+ warnings: snapshot.context.warnings,
26
+ keys: snapshot.context.keys,
27
+ };
28
+ }
29
+ exports.default = default_1;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filterExtractionResult = exports.extractKeysOfFiles = exports.extractKeysFromFile = exports.NullNamespace = void 0;
4
+ const glob_1 = require("glob");
5
+ const worker_1 = require("./worker");
6
+ const util_1 = require("util");
7
+ const glob = (0, util_1.promisify)(glob_1.glob);
8
+ exports.NullNamespace = Symbol('namespace.null');
9
+ async function extractKeysFromFile(file, extractor) {
10
+ return (0, worker_1.callWorker)({
11
+ extractor: extractor,
12
+ file: file,
13
+ });
14
+ }
15
+ exports.extractKeysFromFile = extractKeysFromFile;
16
+ async function extractKeysOfFiles(filesPattern, extractor) {
17
+ const files = await glob(filesPattern, { nodir: true });
18
+ const result = new Map();
19
+ await Promise.all(files.map(async (file) => {
20
+ const keys = await extractKeysFromFile(file, extractor);
21
+ result.set(file, keys);
22
+ }));
23
+ return result;
24
+ }
25
+ exports.extractKeysOfFiles = extractKeysOfFiles;
26
+ function filterExtractionResult(data) {
27
+ const result = Object.create(null);
28
+ for (const { keys } of data.values()) {
29
+ for (const key of keys) {
30
+ const namespace = key.namespace || exports.NullNamespace;
31
+ if (!(namespace in result)) {
32
+ result[namespace] = new Map();
33
+ }
34
+ result[namespace].set(key.keyName, key.defaultValue || null);
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+ exports.filterExtractionResult = filterExtractionResult;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const path_1 = require("path");
4
+ const promises_1 = require("fs/promises");
5
+ const vscode_textmate_1 = require("vscode-textmate");
6
+ const vscode_oniguruma_1 = require("vscode-oniguruma");
7
+ const GRAMMAR_PATH = (0, path_1.join)(__dirname, '..', '..', 'textmate');
8
+ const GrammarFiles = {
9
+ ["source.ts" /* Grammar.TYPESCRIPT */]: (0, path_1.join)(GRAMMAR_PATH, 'TypeScript.tmLanguage'),
10
+ ["source.tsx" /* Grammar.TYPESCRIPT_TSX */]: (0, path_1.join)(GRAMMAR_PATH, 'TypeScriptReact.tmLanguage'),
11
+ };
12
+ let oniguruma;
13
+ let registry;
14
+ async function initializeOniguruma() {
15
+ const wasmBlobPath = require
16
+ .resolve('vscode-oniguruma')
17
+ .replace('main.js', 'onig.wasm');
18
+ const wasmBlob = await (0, promises_1.readFile)(wasmBlobPath);
19
+ await (0, vscode_oniguruma_1.loadWASM)(wasmBlob);
20
+ return {
21
+ createOnigScanner: (patterns) => new vscode_oniguruma_1.OnigScanner(patterns),
22
+ createOnigString: (s) => new vscode_oniguruma_1.OnigString(s),
23
+ };
24
+ }
25
+ async function loadGrammar(scope) {
26
+ const file = GrammarFiles[scope];
27
+ if (!file)
28
+ return null;
29
+ const grammar = await (0, promises_1.readFile)(file, 'utf8');
30
+ return (0, vscode_textmate_1.parseRawGrammar)(grammar);
31
+ }
32
+ function extnameToGrammar(extname) {
33
+ switch (extname) {
34
+ case '.js':
35
+ case '.mjs':
36
+ case '.cjs':
37
+ case '.ts':
38
+ case '.mts':
39
+ case '.cts':
40
+ return "source.ts" /* Grammar.TYPESCRIPT */;
41
+ case '.jsx':
42
+ case '.tsx':
43
+ return "source.tsx" /* Grammar.TYPESCRIPT_TSX */;
44
+ }
45
+ }
46
+ function tokenize(code, grammar) {
47
+ let stack = vscode_textmate_1.INITIAL;
48
+ let linePtr = 0;
49
+ const lines = code.split('\n');
50
+ const tokens = [];
51
+ for (let i = 0; i < lines.length; i++) {
52
+ const line = lines[i];
53
+ const res = grammar.tokenizeLine(line, stack);
54
+ for (const token of res.tokens) {
55
+ // Opinionated take: if a token is scope-less, chances are we don't care about it.
56
+ // Ditching it allows us to reduce complexity from the state machine's POV.
57
+ if (token.scopes.length !== 1) {
58
+ const codeToken = code.slice(linePtr + token.startIndex, linePtr + token.endIndex);
59
+ tokens.push({
60
+ type: token.scopes[token.scopes.length - 1],
61
+ token: codeToken,
62
+ line: i + 1,
63
+ startIndex: linePtr + token.startIndex,
64
+ endIndex: linePtr + token.endIndex,
65
+ scopes: token.scopes,
66
+ });
67
+ }
68
+ }
69
+ tokens.push({
70
+ type: 'newline',
71
+ token: '\n',
72
+ line: i + 1,
73
+ startIndex: linePtr + line.length,
74
+ endIndex: linePtr + line.length + 1,
75
+ scopes: [],
76
+ });
77
+ linePtr += line.length + 1;
78
+ stack = res.ruleStack;
79
+ }
80
+ return tokens;
81
+ }
82
+ async function default_1(code, fileName) {
83
+ if (!oniguruma) {
84
+ // Lazily initialize the WebAssembly runtime
85
+ oniguruma = initializeOniguruma();
86
+ registry = new vscode_textmate_1.Registry({
87
+ onigLib: oniguruma,
88
+ loadGrammar: loadGrammar,
89
+ });
90
+ }
91
+ const fileType = (0, path_1.extname)(fileName);
92
+ const grammarName = extnameToGrammar(fileType);
93
+ if (!grammarName) {
94
+ throw new Error(`Cannot find grammar for file type ${fileType}`);
95
+ }
96
+ const grammar = await registry.loadGrammar(grammarName);
97
+ if (!grammar) {
98
+ throw new Error(`Cannot load grammar ${grammarName}`);
99
+ }
100
+ return tokenize(code, grammar);
101
+ }
102
+ exports.default = default_1;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.emitGitHubWarning = exports.dumpWarnings = exports.WarningMessages = void 0;
4
+ const path_1 = require("path");
5
+ exports.WarningMessages = {
6
+ W_DYNAMIC_KEY: {
7
+ name: 'Dynamic key',
8
+ description: 'The key not static and cannot be extracted automatically. This key will be ignored.\n\nUse @tolgee-key or @tolgee-ignore to suppress this warning.',
9
+ },
10
+ W_DYNAMIC_NAMESPACE: {
11
+ name: 'Dynamic namespace',
12
+ description: 'The namespace for this key is not static and cannot be extracted automatically. This key will be ignored.\n\nUse @tolgee-key or @tolgee-ignore to suppress this warning.',
13
+ },
14
+ W_DYNAMIC_DEFAULT_VALUE: {
15
+ name: 'Dynamic default value',
16
+ description: 'The default value for this key is not static and cannot be extracted automatically.\n\nUse @tolgee-key or @tolgee-ignore to suppress this warning.',
17
+ },
18
+ W_DYNAMIC_OPTIONS: {
19
+ name: 'Dynamic options',
20
+ description: 'The options for this key are not static and cannot be extracted automatically. This key will be ignored.\n\nUse @tolgee-key or @tolgee-ignore to suppress this warning.',
21
+ },
22
+ W_UNRESOLVABLE_NAMESPACE: {
23
+ name: 'Cannot resolve namespace',
24
+ description: 'The namespace for this key cannot be resolved. This key will be ignored.\n\nUse @tolgee-key or @tolgee-ignore to suppress this warning.',
25
+ },
26
+ W_UNUSED_IGNORE: {
27
+ name: 'Unused ignore directive',
28
+ description: 'This directive is unused.',
29
+ },
30
+ W_MALFORMED_KEY_OVERRIDE: {
31
+ name: 'Malformed key override',
32
+ description: "Couldn't parse the JSON5 object.",
33
+ },
34
+ W_INVALID_KEY_OVERRIDE: {
35
+ name: 'Invalid key override',
36
+ description: 'The `key` must be present, and the `key`, `ns`, and `defaultValue` must be strings.',
37
+ },
38
+ };
39
+ /**
40
+ * Dumps warnings emitted during an extraction to stdout, with GitHub integration, and counts them.
41
+ *
42
+ * @param extractionResult Extraction result to dump warnings from.
43
+ * @returns Count of emitted warnings in the extraction.
44
+ */
45
+ function dumpWarnings(extractionResult) {
46
+ let warningCount = 0;
47
+ for (const [file, { warnings }] of extractionResult.entries()) {
48
+ if (warnings.length) {
49
+ if (!warningCount) {
50
+ console.error('Warnings were emitted during extraction.');
51
+ }
52
+ console.error(file);
53
+ for (const warning of warnings) {
54
+ const warnText = warning.warning in exports.WarningMessages
55
+ ? exports.WarningMessages[warning.warning].name
56
+ : warning.warning;
57
+ console.error('\tline %d: %s', warning.line, warnText);
58
+ emitGitHubWarning(warning.warning, file, warning.line);
59
+ }
60
+ warningCount += warnings.length;
61
+ }
62
+ }
63
+ if (warningCount) {
64
+ console.error('Total: %d warning%s', warningCount, warningCount === 1 ? '' : 's');
65
+ }
66
+ return warningCount;
67
+ }
68
+ exports.dumpWarnings = dumpWarnings;
69
+ // TODO: Revisit this function and turn it into an actual GitHub Action Annotation?
70
+ // https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28#update-a-check-run
71
+ function emitGitHubWarning(warning, file, line) {
72
+ if (!process.env.GITHUB_ACTIONS)
73
+ return;
74
+ file = (0, path_1.relative)(process.env.GITHUB_WORKSPACE ?? process.cwd(), file);
75
+ if (warning in exports.WarningMessages) {
76
+ const { name, description } = exports.WarningMessages[warning];
77
+ const encodedDescription = description
78
+ .replaceAll('%', '%25')
79
+ .replaceAll('\r', '%0D')
80
+ .replaceAll('\n', '%0A')
81
+ .replaceAll(':', '%3A')
82
+ .replaceAll(',', '%2C')
83
+ .replaceAll('@', '@​'); // This has a ZWS; preventing mentions
84
+ console.log(`::warning file=${file},line=${line},title=${name}::${encodedDescription}`);
85
+ return;
86
+ }
87
+ console.log(`::warning file=${file},line=${line}::${warning}`);
88
+ }
89
+ exports.emitGitHubWarning = emitGitHubWarning;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.callWorker = void 0;
7
+ const path_1 = require("path");
8
+ const worker_threads_1 = require("worker_threads");
9
+ const promises_1 = require("fs/promises");
10
+ // TODO: this solution won't handle new integrations and it will need a slight tweaking before adding new ones
11
+ const react_1 = __importDefault(require("./presets/react"));
12
+ const moduleLoader_1 = require("../utils/moduleLoader");
13
+ const deferred_1 = require("../utils/deferred");
14
+ const IS_TS_NODE = (0, path_1.extname)(__filename) === '.ts';
15
+ // --- Worker functions
16
+ let loadedExtractor = Symbol('unloaded');
17
+ let extractor;
18
+ async function handleJob(args) {
19
+ if (loadedExtractor !== args.extractor) {
20
+ loadedExtractor = args.extractor;
21
+ extractor = args.extractor
22
+ ? await (0, moduleLoader_1.loadModule)(args.extractor).then((mdl) => mdl.default)
23
+ : react_1.default;
24
+ }
25
+ const file = (0, path_1.resolve)(args.file);
26
+ const code = await (0, promises_1.readFile)(file, 'utf8');
27
+ return extractor(code, file);
28
+ }
29
+ async function workerInit() {
30
+ worker_threads_1.parentPort.on('message', (params) => {
31
+ handleJob(params)
32
+ .then((res) => worker_threads_1.parentPort.postMessage({ data: res }))
33
+ .catch((e) => worker_threads_1.parentPort.postMessage({ err: e }));
34
+ });
35
+ }
36
+ if (!worker_threads_1.isMainThread)
37
+ workerInit();
38
+ // --- Main thread functions
39
+ let worker;
40
+ const jobQueue = [];
41
+ function createWorker() {
42
+ const worker = new worker_threads_1.Worker(__filename, {
43
+ // ts-node workaround
44
+ execArgv: IS_TS_NODE ? ['--require', 'ts-node/register'] : undefined,
45
+ });
46
+ let timeout;
47
+ let currentDeferred;
48
+ function workOrDie() {
49
+ const job = jobQueue.shift();
50
+ if (!job) {
51
+ worker.terminate();
52
+ return;
53
+ }
54
+ worker.postMessage(job[0]);
55
+ currentDeferred = job[1];
56
+ timeout = setTimeout(() => {
57
+ worker.terminate();
58
+ currentDeferred.reject(new Error('aborted'));
59
+ }, 10e3);
60
+ }
61
+ worker.on('message', (msg) => {
62
+ if ('data' in msg) {
63
+ currentDeferred.resolve(msg.data);
64
+ }
65
+ else {
66
+ currentDeferred.reject(msg.err);
67
+ }
68
+ clearTimeout(timeout);
69
+ workOrDie();
70
+ });
71
+ workOrDie();
72
+ return worker;
73
+ }
74
+ async function callWorker(params) {
75
+ const deferred = (0, deferred_1.createDeferred)();
76
+ jobQueue.push([params, deferred]);
77
+ if (!worker) {
78
+ worker = createWorker();
79
+ }
80
+ return deferred.promise;
81
+ }
82
+ exports.callWorker = callWorker;