-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable errors for import/no-extraneous-dependencies. Relying on hoisting makes building Theia application somewhat unstable. In general cases, everything is fine. But as soon as Yarn decides to hoist things differently then it can become difficult to fix. This commit fixes the issue by re-exporting dependencies from @theia/core, this way instead of relying on implicit dependencies one can directly import what @theia/core uses like: import * as dep from '@theia/core/shared/dep' To make this work I added a 'shared' folder under 'packages/core' with js scripts that re-export everything from their matching library. To ease with the development there is a script named 'packages/core/scripts/generate-shared.js' that will read values from 'packages/core/package.json#theiaReExports' in order to more easily add new packages. Depending on how the package exports things we need to re-export things the same way. This same script generates the README.md to list all re-exported packages. It uses README.in.md as a base. @theia/electron now re-exports all members from electron. This prevents having to use electron as an implicit dependency. @theia/electron now also is a @theia/core optional peer dependency. This directly represent the use case we have for this package where we will only use electron features if this package is installed by application makers. If people don't know about this way to consume @theia/core's shared dependencies then nothing will change and they might keep using implicit dependencies. But the new recommended method is to use those re-exports in order to make builds more stable in the face of hoisting and other dependency tree optimizations. Noteworthy: There was an issue with sharing @theia/application-package since I only re-export the main 'index.js' it tried to include Node-specific code in frontend applications. I fixed this by re-exporting @theia/application-package/lib/environment separately. eslint: add @theia/eslint-plugin package Add a new package @theia/eslint-plugin to provide rules for downstream projects to re-use. The goal was simply to give better error messages if committers used implicit dependencies again, by notifying when a shared package is depended upon without going through @theia/core/shared/... It turned out that implementing that rule revealed places where import/no-extraneous-dependencies simply didn't catch. The reason might be that the plugin wasn't designed to work on TS sources. This commit fixes those newly found occurences of implicit dependencies. Signed-off-by: Paul <[email protected]>
- Loading branch information
1 parent
9b98cf9
commit 96930a2
Showing
932 changed files
with
2,302 additions
and
1,278 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
} | ||
}, | ||
"plugins": [ | ||
"@theia", | ||
"@typescript-eslint", | ||
"@typescript-eslint/tslint", | ||
"import", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import Electron = require('electron'); | ||
export = Electron; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('electron'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from 'native-keymap' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('native-keymap'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// @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 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
/** @type {{[ruleId: string]: import('eslint').Rule.RuleModule}} */ | ||
exports.rules = { | ||
"shared-dependencies": require('./rules/shared-dependencies'), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"private": true, | ||
"name": "@theia/eslint-plugin", | ||
"version": "1.11.0", | ||
"description": "Custom ESLint rules for developing Theia extensions and applications", | ||
"dependencies": { | ||
"@theia/core": "1.11.0" | ||
} | ||
} |
171 changes: 171 additions & 0 deletions
171
dev-packages/eslint-plugin/rules/shared-dependencies.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
// @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 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
const fs = require('fs'); | ||
const path = require('path'); | ||
|
||
const { | ||
theiaCoreSharedPrefix, | ||
isSharedModule, | ||
getTheiaCoreSharedModule, | ||
} = require('@theia/core/shared'); | ||
|
||
/** @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 sharedModules = getSharedModuleDependencies(packageJson); | ||
if (sharedModules.length > 0) { | ||
context.report({ | ||
loc: { line: 0, column: 0 }, | ||
message: `"${packageJson.__filename}" depends on some @theia/core shared dependencies: [${sharedModules}]`, | ||
}); | ||
} | ||
} | ||
/** | ||
* @param {import('estree').Literal} node | ||
*/ | ||
function checkModuleImport(node) { | ||
const module = /** @type {string} */(node.value); | ||
if (isSharedModule(module)) { | ||
context.report({ | ||
node, | ||
message: `"${module}" is a @theia/core shared dependency, please use "${theiaCoreSharedPrefix}${module}" 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], theiaCoreSharedPrefix); | ||
} | ||
} | ||
}); | ||
} else { | ||
const shared = getTheiaCoreSharedModule(module); | ||
if (shared && !isSharedModule(shared)) { | ||
context.report({ | ||
node, | ||
message: `"${shared}" is not part of @theia/core shared dependencies.` | ||
}); | ||
} | ||
} | ||
} | ||
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; | ||
} | ||
|
||
/** | ||
* @param {object} packageJson | ||
* @return {string[]} | ||
*/ | ||
function getSharedModuleDependencies(packageJson) { | ||
return typeof packageJson.dependencies === 'object' | ||
? Object.keys(packageJson.dependencies).filter(dependency => isSharedModule(dependency)) | ||
: []; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.