Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PoC - cli: filter Theia extensions #9309

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ export abstract class AbstractGenerator {
if (modules.size === 0) {
return '';
}
const lines = Array.from(modules.keys()).map(moduleName => {
const invocation = `${fn}('${modules.get(moduleName)}')`;
if (fn === 'require') {
return `Promise.resolve(${invocation})`;
}
return invocation;
}).map(statement => ` .then(function () { return ${statement}.then(load) })`);
const lines = Array.from(modules.values(), modulePath => {
const invocation = `${fn}('${modulePath}')`;
const promiseStatement = fn === 'require'
? `Promise.resolve(${invocation})`
: invocation; // the `import` function already returns a Promise
return ` .then(function () { return ${promiseStatement}.then(load); })`;
});
return os.EOL + lines.join(os.EOL);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { AbstractGenerator } from './abstract-generator';
export class BackendGenerator extends AbstractGenerator {

async generate(): Promise<void> {
const backendModules = this.pck.targetBackendModules;
const backendModules = this.pck.targetBackendModulesFiltered;
await this.write(this.pck.backend('server.js'), this.compileServer(backendModules));
await this.write(this.pck.backend('main.js'), this.compileMain(backendModules));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import { existsSync, readFileSync } from 'fs';
export class FrontendGenerator extends AbstractGenerator {

async generate(): Promise<void> {
const frontendModules = this.pck.targetFrontendModules;
const frontendModules = this.pck.targetFrontendModulesFiltered;
await this.write(this.pck.frontend('index.html'), this.compileIndexHtml(frontendModules));
await this.write(this.pck.frontend('index.js'), this.compileIndexJs(frontendModules));
if (this.pck.isElectron()) {
const electronMainModules = this.pck.targetElectronMainModules;
const electronMainModules = this.pck.targetElectronMainModulesFiltered;
await this.write(this.pck.frontend('electron-main.js'), this.compileElectronMain(electronMainModules));
}
}
Expand Down
67 changes: 64 additions & 3 deletions dev-packages/application-package/src/application-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,15 +240,76 @@ export class ApplicationPackage {
}

get targetBackendModules(): Map<string, string> {
return this.ifBrowser(this.backendModules, this.backendElectronModules);
return this.isBrowser() ? this.backendModules : this.backendElectronModules;
}

get targetFrontendModules(): Map<string, string> {
return this.ifBrowser(this.frontendModules, this.frontendElectronModules);
return this.isBrowser() ? this.frontendModules : this.frontendElectronModules;
}

get targetElectronMainModules(): Map<string, string> {
return this.ifElectron(this.electronMainModules, new Map());
return this.isElectron() ? this.electronMainModules : new Map();
}

protected _targetBackendModulesFiltered: Map<string, string>;
get targetBackendModulesFiltered(): Map<string, string> {
if (!this._targetBackendModulesFiltered) {
this._targetBackendModulesFiltered = this.filterModules(this.targetBackendModules);
}
return this._targetBackendModulesFiltered;
}

protected _targetFrontendModulesFiltered: Map<string, string>;
get targetFrontendModulesFiltered(): Map<string, string> {
if (!this._targetFrontendModulesFiltered) {
this._targetFrontendModulesFiltered = this.filterModules(this.targetFrontendModules);
}
return this._targetFrontendModulesFiltered;
}

protected _targetElectronMainModulesFiltered: Map<string, string>;
get targetElectronMainModulesFiltered(): Map<string, string> {
if (!this._targetElectronMainModulesFiltered) {
this._targetElectronMainModulesFiltered = this.filterModules(this.electronMainModules);
}
return this._targetElectronMainModulesFiltered;
}

protected filterModules(modules: Map<string, string>): Map<string, string> {
const { strategy, includes = [], excludes = [] } = this.props.extensions.loading;
const { dependencies = {} } = this.pck;
const all = strategy === 'all';
const explicitDependenciesOnly = strategy === 'explicitDependenciesOnly';
if (!all && !explicitDependenciesOnly) {
throw new Error(`unknown theia.extensions.loading.strategy: ${strategy}`);
}
const filtered = new Map<string, string>();
for (const [name, path] of modules.entries()) {
if (excludes.some(exclude => new RegExp(exclude).test(path))) {
continue;
} else if (
// When using 'all' the include list has precedent:
all && (includes.length === 0 || includes.some(include => new RegExp(include).test(path)))
// Using 'explicitDependenciesOnly' is complementary to the include list, else
// setting 'explicitDependenciesOnly' would be nulled by the include list.
|| explicitDependenciesOnly && (this.getPackageName(path) in dependencies || includes.length > 0 && includes.some(include => new RegExp(include).test(path)))
) {
filtered.set(name, path);
}
}
return filtered;
}

/**
* Only keep the first two parts of the package name e.g.,
* - `@a/b/c/...` => `@a/b`
* - `a/b/c/...` => `a`
*/
protected getPackageName(mod: string): string {
const slice = mod.startsWith('@') ? 2 : 1;
return mod.split('/', slice + 1)
.slice(0, slice)
.join('/');
}

setDependency(name: string, version: string | undefined): boolean {
Expand Down
45 changes: 45 additions & 0 deletions dev-packages/application-package/src/application-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ export interface ApplicationProps extends NpmRegistryProps {
*/
readonly target: ApplicationProps.Target;

/**
* Theia extensions related properties.
*/
readonly extensions: Readonly<ExtensionsConfig>;

/**
* Frontend related properties.
*/
Expand All @@ -75,6 +80,11 @@ export namespace ApplicationProps {
export const DEFAULT: ApplicationProps = {
...NpmRegistryProps.DEFAULT,
target: 'browser',
extensions: {
loading: {
strategy: 'all'
}
},
backend: {
config: {}
},
Expand All @@ -94,6 +104,41 @@ export namespace ApplicationProps {

}

/**
* Configure how Theia handles extensions.
*/
export interface ExtensionsConfig {

/**
* Specify which inversify modules to mount in your application.
*
* Note that the `loadingStrategy` only affects which inversify module will be mounted
* to your application, but an ignored extension might still end up in your bundles.
*/
loading: ExtensionLoadingStrategy;
}

export interface ExtensionLoadingStrategy {

/**
* - `all`: Use all Theia extensions found installed under `node_modules`.
* - `explicitDependenciesOnly`: Only use what's defined as your application dependencies.
*/
strategy: 'all' | 'explicitDependenciesOnly';

/**
* List of regexes to filter in Theia extensions, will be matched against the modules file path.
*
* Note that matched files will be added to what's matched by `explicitDependenciesOnly`.
*/
includes?: string[]

/**
* List of regexes to filter out Theia extensions, will be matched against the modules file path.
*/
excludes?: string[]
}

/**
* Base configuration for the Theia application.
*/
Expand Down