deploy: current vibn theia state
Some checks failed
Playwright Tests / Playwright Tests (ubuntu-22.04, Node.js 22.x) (push) Has been cancelled
3PP License Check / 3PP License Check (11, 22.x, ubuntu-22.04) (push) Has been cancelled
Publish packages to NPM / Perform Publishing (push) Has been cancelled

Made-with: Cursor
This commit is contained in:
2026-02-27 12:01:08 -08:00
commit 8bb5110148
3782 changed files with 640947 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
// @ts-check
// *****************************************************************************
// Copyright (C) 2024 EclipseSource and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/**
* @typedef {import('@typescript-eslint/utils').TSESTree.ClassDeclaration} ClassDeclaration
* @typedef {import('@typescript-eslint/utils').TSESTree.ClassElement} ClassElement
* @typedef {import('@typescript-eslint/utils').TSESTree.Decorator} Decorator
* @typedef {import('@typescript-eslint/utils').TSESTree.MethodDefinition} MethodDefinition
* @typedef {import('@typescript-eslint/utils').TSESTree.Parameter} Parameter
* @typedef {import('estree').Node} Node
* @typedef {import('eslint').Rule.RuleModule} RuleModule
*/
/**
* Type guard to check if a ClassElement is a MethodDefinition.
* @param {ClassElement} element
* @returns {element is MethodDefinition}
*/
function isMethodDefinition(element) {
return element.type === 'MethodDefinition';
}
/** @type {RuleModule} */
module.exports = {
meta: {
type: 'problem',
docs: {
description:
'Ensure @injectable classes have annotated constructor parameters',
},
messages: {
missingAnnotation: 'Constructor parameters in an @injectable class must be annotated with @inject, @unmanaged or @multiInject',
},
},
create(context) {
return {
/**
* @param {ClassDeclaration} node
*/
ClassDeclaration(node) {
// Check if the class has a decorator named `injectable`
const hasInjectableDecorator = node.decorators?.some(
(/** @type {Decorator} */ decorator) =>
decorator.expression.type === 'CallExpression' &&
decorator.expression.callee.type === 'Identifier' &&
decorator.expression.callee.name === 'injectable'
);
if (hasInjectableDecorator) {
// Find the constructor method within the class body
const constructor = node.body.body.find(
member =>
isMethodDefinition(member) &&
member.kind === 'constructor'
);
if (
constructor &&
// We need to re-apply 'isMethodDefinition' here because the type guard is not properly preserved
isMethodDefinition(constructor) &&
constructor.value &&
constructor.value.params.length > 0
) {
constructor.value.params.forEach(
/** @type {Parameter} */ param => {
// Check if each constructor parameter has a decorator
const hasAnnotation = param.decorators?.some(
(/** @type {Decorator} */ decorator) =>
decorator.expression.type === 'CallExpression' &&
decorator.expression.callee.type === 'Identifier' &&
(decorator.expression.callee.name === 'inject' ||
decorator.expression.callee.name === 'unmanaged' ||
decorator.expression.callee.name === 'multiInject')
);
if (!hasAnnotation) {
context.report({
node: /** @type Node */ (param),
messageId: 'missingAnnotation',
});
}
}
);
}
}
},
};
},
};

View File

@@ -0,0 +1,149 @@
// @ts-check
// *****************************************************************************
// Copyright (C) 2021 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
const levenshtein = require('js-levenshtein');
// eslint-disable-next-line import/no-extraneous-dependencies
const metadata = require('@theia/core/src/common/i18n/nls.metadata.json');
const messages = new Set(Object.values(metadata.messages)
.reduceRight((prev, curr) => prev.concat(curr), [])
.map(e => e.replace(/&&/g, '')));
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
fixable: 'code',
docs: {
description: 'prevent incorrect use of \'nls.localize\'.',
},
},
create(context) {
return {
CallExpression(node) {
const callee = node.callee;
if (callee.type === 'Super') {
return;
}
const localizeResults = evaluateLocalize(node);
for (const { value, byDefault, node: localizeNode } of localizeResults) {
if (value !== undefined && localizeNode) {
if (byDefault && !messages.has(value)) {
let lowestDistance = Number.MAX_VALUE;
let lowestMessage = '';
for (const message of messages) {
const distance = levenshtein(value, message);
if (distance < lowestDistance) {
lowestDistance = distance;
lowestMessage = message;
}
}
if (lowestMessage) {
const replacementValue = `'${lowestMessage.replace(/'/g, "\\'").replace(/\n/g, '\\n')}'`;
context.report({
node: localizeNode,
message: `'${value}' is not a valid default value. Did you mean ${replacementValue}?`,
fix: function (fixer) {
return fixer.replaceText(localizeNode, replacementValue);
}
});
} else {
context.report({
node: localizeNode,
message: `'${value}' is not a valid default value.`
});
}
} else if (!byDefault && messages.has(value)) {
context.report({
node,
message: `'${value}' can be translated using the 'nls.localizeByDefault' function.`,
fix: function (fixer) {
const code = context.getSourceCode();
const args = node.arguments.slice(1);
const argsCode = args.map(e => code.getText(e)).join(', ');
const updatedCall = `nls.localizeByDefault(${argsCode})`;
return fixer.replaceText(node, updatedCall);
}
});
}
}
}
}
};
/**
* Evaluates a call expression and returns localization info.
* @param {import('estree').CallExpression} node
* @returns {Array<{value?: string, byDefault: boolean, node?: import('estree').Node}>}
*/
function evaluateLocalize(/** @type {import('estree').CallExpression} */ node) {
const callee = node.callee;
if ('object' in callee && 'name' in callee.object && 'property' in callee && 'name' in callee.property && callee.object.name === 'nls') {
if (callee.property.name === 'localize') {
const defaultTextNode = node.arguments[1]; // The default text node is the second argument for `nls.localize`
if (defaultTextNode && defaultTextNode.type === 'Literal' && typeof defaultTextNode.value === 'string') {
return [{
node: defaultTextNode,
value: defaultTextNode.value,
byDefault: false
}];
}
} else if (callee.property.name === 'localizeByDefault') {
const defaultTextNode = node.arguments[0]; // The default text node is the first argument for `nls.localizeByDefault`
if (defaultTextNode && defaultTextNode.type === 'Literal' && typeof defaultTextNode.value === 'string') {
return [{
node: defaultTextNode,
value: defaultTextNode.value,
byDefault: true
}];
}
}
}
// Check for Command.toDefaultLocalizedCommand
if ('object' in callee && 'name' in callee.object && 'property' in callee && 'name' in callee.property
&& callee.object.name === 'Command' && callee.property.name === 'toDefaultLocalizedCommand') {
const commandArg = node.arguments[0];
if (commandArg && commandArg.type === 'ObjectExpression') {
return extractDefaultLocalizedProperties(commandArg);
}
}
return [];
}
/**
* Extracts label and category properties from a Command object that will be passed to localizeByDefault.
* @param {import('estree').ObjectExpression} objectNode
* @returns {Array<{value?: string, byDefault: boolean, node?: import('estree').Node}>}
*/
function extractDefaultLocalizedProperties(objectNode) {
const results = [];
for (const property of objectNode.properties) {
if (property.type === 'Property' && property.key.type === 'Identifier') {
const keyName = property.key.name;
if ((keyName === 'label' || keyName === 'category') && property.value.type === 'Literal' && typeof property.value.value === 'string') {
results.push({
node: property.value,
value: property.value.value,
byDefault: true
});
}
}
}
return results;
}
}
};

View File

@@ -0,0 +1,54 @@
// @ts-check
// *****************************************************************************
// Copyright (C) 2021 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
const path = require('path');
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
fixable: 'code',
docs: {
description: 'prevent imports from \'src\'.',
},
},
create(context) {
return {
ImportDeclaration(node) {
checkModuleImport(node.source);
},
TSExternalModuleReference(node) {
checkModuleImport(node.expression);
},
};
function checkModuleImport(node) {
const module = /** @type {string} */(node.value);
const extension = path.parse(module).ext;
const re = /^@theia\/\S+\/src\//;
if (re.test(module) && extension === '') {
context.report({
node,
message: `'${module}' should not be imported with '/src/'`,
fix: function (fixer) {
const updatedModule = `'${module.replace('/src/', '/lib/')}'`;
return fixer.replaceText(node, updatedModule);
}
});
}
}
}
};

View File

@@ -0,0 +1,119 @@
// @ts-check
// *****************************************************************************
// Copyright (C) 2021 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/* eslint-disable max-len */
const path = require('path');
/**
* Runtime-specific folders according to our coding guidelines.
*/
const folders = {
common: 'common',
browser: 'browser',
node: 'node',
electronCommon: 'electron-common',
electronBrowser: 'electron-browser',
electronNode: 'electron-node',
electronMain: 'electron-main',
};
/**
* @typedef {object} ImportRule
* @property {string[]} allowed
* @property {string[]} restricted
*/
/**
* @param {string} src
* @param {string[]} allowedFolders
* @returns {[string, ImportRule]}
*/
function allow(src, allowedFolders) {
const allowed = [src, ...allowedFolders];
const restricted = Object.values(folders).filter(folder => !allowed.includes(folder));
return [src, { allowed, restricted }];
}
/**
* Mapping of folders to the list of allowed/restricted folders to import from.
* @type {[string, ImportRule][]}
*/
const importRuleMapping = [
allow(folders.common, []),
allow(folders.browser, [folders.common]),
allow(folders.node, [folders.common]),
allow(folders.electronCommon, [folders.common]),
allow(folders.electronBrowser, [folders.electronCommon, folders.browser, folders.common]),
allow(folders.electronNode, [folders.electronCommon, folders.node, folders.common]),
allow(folders.electronMain, [folders.electronCommon, folders.node, folders.common]),
];
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
fixable: 'code',
docs: {
description: 'prevent imports from folders meant for incompatible runtimes.',
url: 'https://github.com/eclipse-theia/theia/tree/master/doc/code-organization.md'
},
},
create(context) {
let relativeFilePath = path.relative(context.getCwd(), context.getFilename());
// Normalize the path so we only deal with forward slashes.
if (process.platform === 'win32') {
relativeFilePath = relativeFilePath.replace(/\\/g, '/');
}
// Search for a folder following our naming conventions, keep the left-most match.
// e.g. `src/electron-node/browser/node/...` should match `electron-node`
let lowestIndex = Infinity;
/** @type {ImportRule | undefined} */
let matchedImportRule;
/** @type {string | undefined} */
let matchedFolder;
for (const [folder, importRule] of importRuleMapping) {
const index = relativeFilePath.indexOf(`/${folder}/`);
if (index !== -1 && index < lowestIndex) {
matchedImportRule = importRule;
matchedFolder = folder;
lowestIndex = index;
}
}
// File doesn't follow our naming convention so we'll bail now.
if (matchedFolder === undefined) {
return {};
}
return {
ImportDeclaration(node) {
checkModuleImport(node.source);
},
TSExternalModuleReference(node) {
checkModuleImport(node.expression);
},
};
function checkModuleImport(node) {
const module = /** @type {string} */(node.value);
if (matchedImportRule.restricted.some(restricted => module.includes(`/${restricted}/`) || module.endsWith(`/${restricted}`))) {
context.report({
node,
message: `'${module}' cannot be imported in '${matchedFolder}', only '${matchedImportRule.allowed.join(', ')}' ${matchedImportRule.allowed.length === 1 ? 'is' : 'are'} allowed.`
});
}
}
},
};

View File

@@ -0,0 +1,164 @@
// @ts-check
// *****************************************************************************
// Copyright (C) 2021 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/* eslint-disable max-len */
const fs = require('fs');
const path = require('path');
const { PackageReExports } = require('@theia/re-exports');
const coreReExports = PackageReExports.FromPackageSync('@theia/core');
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
fixable: 'code',
docs: {
description: 'Errors when a dependency shared by @theia/core is used implicitly, or when a package depends on a shared dependency instead of reusing it from @theia/core/shared. This rule only affects files from packages that depend on @theia/core.',
recommended: true,
},
},
create(context) {
const filename = context.getFilename();
const packageJson = findPackageJson(filename);
if (packageJson && dependsOnTheiaCore(packageJson)) {
// Only show an error regarding the package.json file if this is the first
// time we detect the error, else it will error for every file of the package:
if (firstTime(packageJson.__filename)) {
const redundantDeps = getRedundantDependencies(packageJson);
if (redundantDeps.length > 0) {
context.report({
loc: { line: 0, column: 0 },
message: `"${packageJson.__filename}" depends on some @theia/core shared dependencies: [${redundantDeps}]`,
});
}
}
function checkModuleImport(node) {
const moduleName = /** @type {string} */(node.value);
const reExport = coreReExports.findReExportByModuleName(moduleName);
if (reExport) {
context.report({
node,
message: `"${moduleName}" is a @theia/core shared dependency, please use "${reExport.externalImport}" instead.`,
fix(fixer) {
if (node.range) {
const [start, end] = node.range;
// Make sure to insert text between the first quote of the string and the rest:
return fixer.insertTextBeforeRange([start + 1, end], `${coreReExports.packageName}/${reExport.reExportDir}`);
}
}
});
}
}
return {
ImportDeclaration(node) {
checkModuleImport(node.source);
},
TSExternalModuleReference(node) {
checkModuleImport(node.expression);
},
};
}
return {};
},
};
/** @type {Set<string>} */
const firstTimeCache = new Set();
/**
* @param {string} key
* @returns {boolean} true if first time seeing `key` else false.
*/
function firstTime(key) {
if (firstTimeCache.has(key)) {
return false;
} else {
firstTimeCache.add(key);
return true;
}
}
/**
* @typedef FoundPackageJson
* @property {string} __filename
* @property {{[package: string]: string}} [dependencies]
*/
/**
* Keep a shortcut to a given package.json file based on previous crawls.
* @type {Map<string, FoundPackageJson>}
*/
const findPackageJsonCache = new Map();
/**
* @param {string} from file path to start searching from.
* @returns {FoundPackageJson | undefined}
*/
function findPackageJson(from) {
from = path.resolve(from);
let current = fs.statSync(from).isDirectory() ? from : path.dirname(from);
// Keep track of all paths tried before eventually finding a package.json file
const tried = [current];
while (!isRoot(path.parse(from))) {
const cached = findPackageJsonCache.get(current);
if (cached) {
return cached;
}
const packageJsonPath = path.resolve(current, 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, { encoding: 'utf8' }));
for (const dir of tried) {
findPackageJsonCache.set(dir, packageJson);
}
packageJson['__filename'] = packageJsonPath;
return packageJson;
}
current = path.dirname(current);
tried.push(current);
}
}
/**
* @param {path.ParsedPath} parsed
* @returns {boolean}
*/
function isRoot(parsed) {
return parsed.base === '' && parsed.dir === parsed.root;
}
/**
* @param {object} packageJson
* @returns {boolean}
*/
function dependsOnTheiaCore(packageJson) {
return typeof packageJson.dependencies === 'object'
&& '@theia/core' in packageJson.dependencies;
}
/**
* Return a list of packages from `packageJson`'s dependencies that can be
* required using `@theia/core/(electron-)shared/...`.
* @param {object} packageJson
* @return {string[]}
*/
function getRedundantDependencies(packageJson) {
return typeof packageJson.dependencies === 'object'
? Object.keys(packageJson.dependencies).filter(
dependency => coreReExports.findReExportsByPackageName(dependency).length > 0
)
: [];
}