@theia/plugin-utils 1.75.0-next.28

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 (122) hide show
  1. package/README.md +30 -0
  2. package/lib/common/constants.d.ts +12 -0
  3. package/lib/common/constants.d.ts.map +1 -0
  4. package/lib/common/constants.js +30 -0
  5. package/lib/common/constants.js.map +1 -0
  6. package/lib/common/constants.spec.d.ts +2 -0
  7. package/lib/common/constants.spec.d.ts.map +1 -0
  8. package/lib/common/constants.spec.js +33 -0
  9. package/lib/common/constants.spec.js.map +1 -0
  10. package/lib/common/contribution-types.d.ts +648 -0
  11. package/lib/common/contribution-types.d.ts.map +1 -0
  12. package/lib/common/contribution-types.js +30 -0
  13. package/lib/common/contribution-types.js.map +1 -0
  14. package/lib/common/manifest-types.d.ts +105 -0
  15. package/lib/common/manifest-types.d.ts.map +1 -0
  16. package/lib/common/manifest-types.js +31 -0
  17. package/lib/common/manifest-types.js.map +1 -0
  18. package/lib/common/manifest-types.spec.d.ts +2 -0
  19. package/lib/common/manifest-types.spec.d.ts.map +1 -0
  20. package/lib/common/manifest-types.spec.js +35 -0
  21. package/lib/common/manifest-types.spec.js.map +1 -0
  22. package/lib/common/package-nls.d.ts +24 -0
  23. package/lib/common/package-nls.d.ts.map +1 -0
  24. package/lib/common/package-nls.js +82 -0
  25. package/lib/common/package-nls.js.map +1 -0
  26. package/lib/common/package-nls.spec.d.ts +2 -0
  27. package/lib/common/package-nls.spec.d.ts.map +1 -0
  28. package/lib/common/package-nls.spec.js +98 -0
  29. package/lib/common/package-nls.spec.js.map +1 -0
  30. package/lib/common/plugin-activation-events.d.ts +7 -0
  31. package/lib/common/plugin-activation-events.d.ts.map +1 -0
  32. package/lib/common/plugin-activation-events.js +95 -0
  33. package/lib/common/plugin-activation-events.js.map +1 -0
  34. package/lib/common/plugin-activation-events.spec.d.ts +2 -0
  35. package/lib/common/plugin-activation-events.spec.d.ts.map +1 -0
  36. package/lib/common/plugin-activation-events.spec.js +89 -0
  37. package/lib/common/plugin-activation-events.spec.js.map +1 -0
  38. package/lib/common/plugin-manifest.d.ts +10 -0
  39. package/lib/common/plugin-manifest.d.ts.map +1 -0
  40. package/lib/common/plugin-manifest.js +35 -0
  41. package/lib/common/plugin-manifest.js.map +1 -0
  42. package/lib/common/plugin-manifest.spec.d.ts +2 -0
  43. package/lib/common/plugin-manifest.spec.d.ts.map +1 -0
  44. package/lib/common/plugin-manifest.spec.js +83 -0
  45. package/lib/common/plugin-manifest.spec.js.map +1 -0
  46. package/lib/common/plugin-model.d.ts +33 -0
  47. package/lib/common/plugin-model.d.ts.map +1 -0
  48. package/lib/common/plugin-model.js +166 -0
  49. package/lib/common/plugin-model.js.map +1 -0
  50. package/lib/common/plugin-model.spec.d.ts +2 -0
  51. package/lib/common/plugin-model.spec.d.ts.map +1 -0
  52. package/lib/common/plugin-model.spec.js +204 -0
  53. package/lib/common/plugin-model.spec.js.map +1 -0
  54. package/lib/common/protocol-shims.d.ts +128 -0
  55. package/lib/common/protocol-shims.d.ts.map +1 -0
  56. package/lib/common/protocol-shims.js +29 -0
  57. package/lib/common/protocol-shims.js.map +1 -0
  58. package/lib/common/test-helpers.d.ts +5 -0
  59. package/lib/common/test-helpers.d.ts.map +1 -0
  60. package/lib/common/test-helpers.js +41 -0
  61. package/lib/common/test-helpers.js.map +1 -0
  62. package/lib/common/utils.d.ts +20 -0
  63. package/lib/common/utils.d.ts.map +1 -0
  64. package/lib/common/utils.js +74 -0
  65. package/lib/common/utils.js.map +1 -0
  66. package/lib/common/utils.spec.d.ts +2 -0
  67. package/lib/common/utils.spec.d.ts.map +1 -0
  68. package/lib/common/utils.spec.js +77 -0
  69. package/lib/common/utils.spec.js.map +1 -0
  70. package/lib/node/normalize-contributions.d.ts +54 -0
  71. package/lib/node/normalize-contributions.d.ts.map +1 -0
  72. package/lib/node/normalize-contributions.js +985 -0
  73. package/lib/node/normalize-contributions.js.map +1 -0
  74. package/lib/node/normalize-contributions.spec.d.ts +2 -0
  75. package/lib/node/normalize-contributions.spec.d.ts.map +1 -0
  76. package/lib/node/normalize-contributions.spec.js +850 -0
  77. package/lib/node/normalize-contributions.spec.js.map +1 -0
  78. package/lib/node/package-nls.d.ts +3 -0
  79. package/lib/node/package-nls.d.ts.map +1 -0
  80. package/lib/node/package-nls.js +45 -0
  81. package/lib/node/package-nls.js.map +1 -0
  82. package/lib/node/plugin-manifest.d.ts +6 -0
  83. package/lib/node/plugin-manifest.d.ts.map +1 -0
  84. package/lib/node/plugin-manifest.js +26 -0
  85. package/lib/node/plugin-manifest.js.map +1 -0
  86. package/lib/node/plugin-model.d.ts +5 -0
  87. package/lib/node/plugin-model.d.ts.map +1 -0
  88. package/lib/node/plugin-model.js +36 -0
  89. package/lib/node/plugin-model.js.map +1 -0
  90. package/lib/node/read-grammars.d.ts +10 -0
  91. package/lib/node/read-grammars.d.ts.map +1 -0
  92. package/lib/node/read-grammars.js +83 -0
  93. package/lib/node/read-grammars.js.map +1 -0
  94. package/lib/node/read-grammars.spec.d.ts +2 -0
  95. package/lib/node/read-grammars.spec.d.ts.map +1 -0
  96. package/lib/node/read-grammars.spec.js +109 -0
  97. package/lib/node/read-grammars.spec.js.map +1 -0
  98. package/package.json +42 -0
  99. package/src/common/constants.spec.ts +34 -0
  100. package/src/common/constants.ts +35 -0
  101. package/src/common/contribution-types.ts +677 -0
  102. package/src/common/manifest-types.spec.ts +36 -0
  103. package/src/common/manifest-types.ts +126 -0
  104. package/src/common/package-nls.spec.ts +122 -0
  105. package/src/common/package-nls.ts +95 -0
  106. package/src/common/plugin-activation-events.spec.ts +104 -0
  107. package/src/common/plugin-activation-events.ts +102 -0
  108. package/src/common/plugin-manifest.spec.ts +94 -0
  109. package/src/common/plugin-manifest.ts +44 -0
  110. package/src/common/plugin-model.spec.ts +246 -0
  111. package/src/common/plugin-model.ts +194 -0
  112. package/src/common/protocol-shims.ts +148 -0
  113. package/src/common/test-helpers.ts +44 -0
  114. package/src/common/utils.spec.ts +86 -0
  115. package/src/common/utils.ts +81 -0
  116. package/src/node/normalize-contributions.spec.ts +931 -0
  117. package/src/node/normalize-contributions.ts +1098 -0
  118. package/src/node/package-nls.ts +45 -0
  119. package/src/node/plugin-manifest.ts +30 -0
  120. package/src/node/plugin-model.ts +33 -0
  121. package/src/node/read-grammars.spec.ts +128 -0
  122. package/src/node/read-grammars.ts +97 -0
@@ -0,0 +1,45 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2021 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import * as path from 'path';
18
+ import * as fs from 'fs-extra';
19
+ import { isENOENT } from '../common/utils';
20
+ import {
21
+ coerceLocalizations,
22
+ type PackageTranslation,
23
+ } from '../common/package-nls';
24
+
25
+ export async function loadPackageTranslations(pluginPath: string, locale?: string): Promise<PackageTranslation> {
26
+ const defaultPath = path.join(pluginPath, 'package.nls.json');
27
+ try {
28
+ const defaultValue = coerceLocalizations(await fs.readJson(defaultPath));
29
+ if (locale) {
30
+ const localizedPath = path.join(pluginPath, `package.nls.${locale}.json`);
31
+ if (await fs.pathExists(localizedPath)) {
32
+ return {
33
+ translation: coerceLocalizations(await fs.readJson(localizedPath)),
34
+ default: defaultValue
35
+ };
36
+ }
37
+ }
38
+ return { default: defaultValue };
39
+ } catch (error) {
40
+ if (isENOENT(error)) {
41
+ return {};
42
+ }
43
+ throw error;
44
+ }
45
+ }
@@ -0,0 +1,30 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 Maksim Kachurin and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import * as path from 'path';
18
+ import * as fs from 'fs-extra';
19
+ import type { PluginManifest } from '../common/manifest-types';
20
+ import { prepareLoadedManifest, type PrepareLoadedManifestOptions } from '../common/plugin-manifest';
21
+
22
+ type LoadedManifest = Pick<PluginManifest, 'name' | 'publisher' | 'contributes' | 'activationEvents'>;
23
+
24
+ export async function loadManifest<T extends LoadedManifest = PluginManifest>(
25
+ pluginPath: string,
26
+ options?: PrepareLoadedManifestOptions
27
+ ): Promise<T> {
28
+ const manifest = await fs.readJson(path.join(pluginPath, 'package.json')) as T;
29
+ return prepareLoadedManifest(manifest, options);
30
+ }
@@ -0,0 +1,33 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 Maksim Kachurin and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { readdirSync } from 'fs';
18
+ import { toPluginUrl, type PluginIdentifierSource } from '../common/plugin-model';
19
+
20
+ export function getPluginRootFileUrl(manifest: PluginIdentifierSource & { packagePath: string }, names: string[]): string | undefined {
21
+ const nameSet = new Set(names.map(n => n.toLowerCase()));
22
+ try {
23
+ const dir = readdirSync(manifest.packagePath, { withFileTypes: true });
24
+ for (const dirent of dir) {
25
+ if (dirent.isFile() && nameSet.has(dirent.name.toLowerCase())) {
26
+ return toPluginUrl(manifest, dirent.name);
27
+ }
28
+ }
29
+ } catch {
30
+ return undefined;
31
+ }
32
+ return undefined;
33
+ }
@@ -0,0 +1,128 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 Maksim Kachurin and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { expect } from 'chai';
18
+ import * as fs from 'fs-extra';
19
+ import * as os from 'os';
20
+ import * as path from 'path';
21
+ import { readGrammarFromDisk, getGrammarContributionValidationError } from './read-grammars';
22
+
23
+ describe('readGrammarFromDisk', () => {
24
+
25
+ let pluginRoot: string;
26
+
27
+ beforeEach(async () => {
28
+ pluginRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'read-grammars-'));
29
+ });
30
+
31
+ afterEach(async () => {
32
+ await fs.remove(pluginRoot);
33
+ });
34
+
35
+ it('inlines JSON grammar file content', async () => {
36
+ await fs.writeJson(path.join(pluginRoot, 'sample.tmLanguage.json'), { scopeName: 'source.sample' });
37
+
38
+ const grammar = await readGrammarFromDisk({
39
+ language: 'sample',
40
+ scopeName: 'source.sample',
41
+ path: './sample.tmLanguage.json'
42
+ }, pluginRoot);
43
+
44
+ expect(grammar).to.deep.equal({
45
+ language: 'sample',
46
+ scope: 'source.sample',
47
+ format: 'json',
48
+ grammar: { scopeName: 'source.sample' },
49
+ grammarLocation: './sample.tmLanguage.json',
50
+ injectTo: undefined,
51
+ embeddedLanguages: undefined,
52
+ tokenTypes: undefined,
53
+ balancedBracketScopes: undefined,
54
+ unbalancedBracketScopes: undefined
55
+ });
56
+ });
57
+
58
+ it('passes through balanced and unbalanced bracket scopes', async () => {
59
+ await fs.writeJson(path.join(pluginRoot, 'sample.tmLanguage.json'), { scopeName: 'source.sample' });
60
+
61
+ const grammar = await readGrammarFromDisk({
62
+ language: 'sample',
63
+ scopeName: 'source.sample',
64
+ path: './sample.tmLanguage.json',
65
+ balancedBracketScopes: ['*'],
66
+ unbalancedBracketScopes: ['meta.template.expression']
67
+ }, pluginRoot);
68
+
69
+ expect(grammar!.balancedBracketScopes).to.deep.equal(['*']);
70
+ expect(grammar!.unbalancedBracketScopes).to.deep.equal(['meta.template.expression']);
71
+ });
72
+
73
+ it('reads plist grammar files as text', async () => {
74
+ const plist = '<?xml version="1.0"?><plist/>';
75
+ await fs.writeFile(path.join(pluginRoot, 'sample.tmLanguage'), plist);
76
+
77
+ const grammar = await readGrammarFromDisk({
78
+ scopeName: 'source.sample',
79
+ path: './sample.tmLanguage'
80
+ }, pluginRoot);
81
+
82
+ expect(grammar!.format).to.equal('plist');
83
+ expect(grammar!.grammar).to.equal(plist);
84
+ });
85
+
86
+ it('returns undefined for invalid manifest fields and reports via onError', async () => {
87
+ await fs.writeJson(path.join(pluginRoot, 'sample.tmLanguage.json'), { scopeName: 'source.sample' });
88
+ const errors: Array<{ type: string; err: unknown }> = [];
89
+ const onError = (type: string, err: unknown): void => { errors.push({ type, err }); };
90
+
91
+ expect(await readGrammarFromDisk({
92
+ scopeName: '',
93
+ path: './sample.tmLanguage.json'
94
+ }, pluginRoot, { onError })).to.equal(undefined);
95
+
96
+ expect(await readGrammarFromDisk({
97
+ scopeName: 'source.sample',
98
+ path: ''
99
+ }, pluginRoot, { onError })).to.equal(undefined);
100
+
101
+ expect(await readGrammarFromDisk({
102
+ scopeName: 'source.sample',
103
+ path: './sample.tmLanguage.json',
104
+ injectTo: 'text.html.basic' as unknown as string[]
105
+ }, pluginRoot, { onError })).to.equal(undefined);
106
+
107
+ expect(errors).to.have.length(3);
108
+ expect(errors.every(e => e.type === 'grammars')).to.equal(true);
109
+ expect(String(errors[0].err)).to.contain('scopeName');
110
+ expect(String(errors[1].err)).to.contain('path');
111
+ expect(String(errors[2].err)).to.contain('injectTo');
112
+ });
113
+
114
+ it('rejects grammar paths outside the plugin directory with onError', async () => {
115
+ expect(getGrammarContributionValidationError({
116
+ scopeName: 'source.sample',
117
+ path: '../outside.tmLanguage.json'
118
+ }, pluginRoot)).to.contain('escapes the plugin directory');
119
+
120
+ const errors: unknown[] = [];
121
+ expect(await readGrammarFromDisk({
122
+ scopeName: 'source.sample',
123
+ path: '../outside.tmLanguage.json'
124
+ }, pluginRoot, { onError: (_type, err) => errors.push(err) })).to.equal(undefined);
125
+
126
+ expect(String(errors[0])).to.contain('escapes the plugin directory');
127
+ });
128
+ });
@@ -0,0 +1,97 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 Maksim Kachurin and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import * as path from 'path';
18
+ import * as fs from 'fs-extra';
19
+ import { isObject } from '../common/utils';
20
+ import type {
21
+ GrammarsContribution,
22
+ NormalizeContributionsContext,
23
+ PluginPackageGrammarsContribution,
24
+ } from '../common/contribution-types';
25
+
26
+ export type { GrammarsContribution } from '../common/contribution-types';
27
+
28
+ /**
29
+ * Returns a validation error message, or `undefined` if the contribution is valid.
30
+ * Mirrors VS Code `validateGrammarExtensionPoint` manifest checks (without language registry lookup),
31
+ * plus a path-containment guard against escaping the plugin directory.
32
+ */
33
+ export function getGrammarContributionValidationError(
34
+ rawGrammar: PluginPackageGrammarsContribution,
35
+ pluginPath: string
36
+ ): string | undefined {
37
+ if (typeof rawGrammar.scopeName !== 'string' || rawGrammar.scopeName.length === 0) {
38
+ return `Invalid grammar contribution: 'scopeName' must be a non-empty string (path: ${String(rawGrammar.path)}).`;
39
+ }
40
+ if (typeof rawGrammar.path !== 'string' || rawGrammar.path.length === 0) {
41
+ return `Invalid grammar contribution '${rawGrammar.scopeName}': 'path' must be a non-empty string.`;
42
+ }
43
+ if (rawGrammar.language !== undefined && typeof rawGrammar.language !== 'string') {
44
+ return `Invalid grammar contribution '${rawGrammar.scopeName}': 'language' must be a string when set.`;
45
+ }
46
+ if (rawGrammar.injectTo !== undefined
47
+ && (!Array.isArray(rawGrammar.injectTo) || rawGrammar.injectTo.some(scope => typeof scope !== 'string'))) {
48
+ return `Invalid grammar contribution '${rawGrammar.scopeName}': 'injectTo' must be a string array when set.`;
49
+ }
50
+ if (rawGrammar.embeddedLanguages !== undefined && !isObject(rawGrammar.embeddedLanguages)) {
51
+ return `Invalid grammar contribution '${rawGrammar.scopeName}': 'embeddedLanguages' must be an object when set.`;
52
+ }
53
+ if (rawGrammar.tokenTypes !== undefined && !isObject(rawGrammar.tokenTypes)) {
54
+ return `Invalid grammar contribution '${rawGrammar.scopeName}': 'tokenTypes' must be an object when set.`;
55
+ }
56
+ const resolvedPluginPath = path.resolve(pluginPath);
57
+ const grammarPath = path.resolve(resolvedPluginPath, rawGrammar.path);
58
+ const relativePath = path.relative(resolvedPluginPath, grammarPath);
59
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
60
+ return `Invalid grammar contribution '${rawGrammar.scopeName}': path '${rawGrammar.path}' escapes the plugin directory.`;
61
+ }
62
+ return undefined;
63
+ }
64
+
65
+ export async function readGrammarFromDisk(
66
+ rawGrammar: PluginPackageGrammarsContribution,
67
+ pluginPath: string,
68
+ log?: Pick<NormalizeContributionsContext, 'onError'>
69
+ ): Promise<GrammarsContribution | undefined> {
70
+ const validationError = getGrammarContributionValidationError(rawGrammar, pluginPath);
71
+ if (validationError) {
72
+ log?.onError('grammars', validationError);
73
+ return undefined;
74
+ }
75
+
76
+ let grammar: string | object;
77
+ const grammarPath = path.resolve(pluginPath, rawGrammar.path);
78
+
79
+ if (rawGrammar.path.endsWith('json')) {
80
+ grammar = await fs.readJson(grammarPath);
81
+ } else {
82
+ grammar = await fs.readFile(grammarPath, 'utf8');
83
+ }
84
+
85
+ return {
86
+ language: rawGrammar.language,
87
+ scope: rawGrammar.scopeName,
88
+ format: rawGrammar.path.endsWith('json') ? 'json' : 'plist',
89
+ grammar,
90
+ grammarLocation: rawGrammar.path,
91
+ injectTo: rawGrammar.injectTo,
92
+ embeddedLanguages: rawGrammar.embeddedLanguages,
93
+ tokenTypes: rawGrammar.tokenTypes,
94
+ balancedBracketScopes: rawGrammar.balancedBracketScopes,
95
+ unbalancedBracketScopes: rawGrammar.unbalancedBracketScopes
96
+ };
97
+ }