Skip to content

[feature]: allow to install node modules from GitHub url too #2797

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

Merged
merged 8 commits into from
Jun 11, 2024
Merged
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
## __WORK IN PROGRESS__
-->

## 6.0.1 (2024-06-10) - Kiera
## __WORK IN PROGRESS__ - Kiera

**Breaking changes**
* Support for Node.js 16 is dropped!
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ if (result.success) {
}
```

Instead of specifying a module name you can also specify a URL to e.g. install a package from GitHub.
To use the installed node module, you can import it:

```typescript
Expand Down
4 changes: 2 additions & 2 deletions packages/adapter/src/lib/_Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,6 @@ export interface InstallNodeModuleOptions {
}

export interface InternalInstallNodeModuleOptions extends InstallNodeModuleOptions {
/** Name of the npm module */
moduleName: string;
/** Name of the npm module or an installable url ẁorking with `npm install` */
moduleNameOrUrl: string;
}
28 changes: 19 additions & 9 deletions packages/adapter/src/lib/adapter/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
getSupportedFeatures,
isMessageboxSupported,
getAdapterScopedPackageIdentifier,
listInstalledNodeModules
listInstalledNodeModules,
requestModuleNameByUrl
} from '@/lib/adapter/utils.js';
// @ts-expect-error no ts file
import extend from 'node.extend';
Expand Down Expand Up @@ -1249,21 +1250,30 @@
/**
* Install specified npm module
*
* @param moduleName name of the node module
* @param moduleNameOrUrl name of the node module or GitHub url which can be passed to `npm install`
* @param options version information
*/
installNodeModule(moduleName: unknown, options: unknown): Promise<CommandResult> {
Validator.assertString(moduleName, 'moduleName');
installNodeModule(moduleNameOrUrl: unknown, options: unknown): Promise<CommandResult> {
Validator.assertString(moduleNameOrUrl, 'moduleNameOrUrl');
Validator.assertObject<InstallNodeModuleOptions>(options, 'options');

return this._installNodeModule({ ...options, moduleName });
return this._installNodeModule({ ...options, moduleNameOrUrl });
}

private _installNodeModule(options: InternalInstallNodeModuleOptions): Promise<CommandResult> {
const { moduleName, version } = options;
private async _installNodeModule(options: InternalInstallNodeModuleOptions): Promise<CommandResult> {
const { moduleNameOrUrl, version } = options;

let moduleName = moduleNameOrUrl;
const isUrl = URL.canParse(moduleNameOrUrl);

if (isUrl) {
moduleName = await requestModuleNameByUrl(moduleNameOrUrl);
}

const internalModuleName = getAdapterScopedPackageIdentifier({ moduleName, namespace: this.namespace });
return tools.installNodeModule(`${internalModuleName}@npm:${moduleName}@${version}`);
const packageIdentifier = isUrl ? moduleNameOrUrl : `npm:${moduleName}@${version}`;

return tools.installNodeModule(`${internalModuleName}@${packageIdentifier}`);
}

/**
Expand Down Expand Up @@ -1310,7 +1320,7 @@
* Decrypt the password/value with given key
*
* @param secretVal to use for decrypt (or value if only one parameter is given)
* @param [value] value to decrypt (if secret is provided)
* @param value value to decrypt (if secret is provided)
*/
decrypt(secretVal: unknown, value?: unknown): string {
if (value === undefined) {
Expand Down Expand Up @@ -1585,7 +1595,7 @@
* ...
* }
* ```

Check warning on line 1598 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Expected JSDoc block to be aligned
* @param featureName the name of the feature to check
* @returns true/false if the feature is in the list of supported features
*/
Expand Down Expand Up @@ -5120,11 +5130,11 @@
): void;

/**
* @param deviceName

Check warning on line 5133 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Missing JSDoc @param "deviceName" description
* @param common

Check warning on line 5134 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Missing JSDoc @param "common" description
* @param _native

Check warning on line 5135 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Missing JSDoc @param "_native" description
* @param options

Check warning on line 5136 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Missing JSDoc @param "options" description
* @param callback

Check warning on line 5137 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Missing JSDoc @param "callback" description
* @deprecated use `this.extendObject` instead
*/
createDevice(deviceName: unknown, common: unknown, _native?: unknown, options?: unknown, callback?: unknown): any {
Expand Down Expand Up @@ -5215,10 +5225,10 @@
/**
* Name of channel must be in format "channel"
*
* @param parentDevice

Check warning on line 5228 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Missing JSDoc @param "parentDevice" description
* @param channelName

Check warning on line 5229 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Missing JSDoc @param "channelName" description
* @param roleOrCommon

Check warning on line 5230 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Missing JSDoc @param "roleOrCommon" description
* @param _native

Check warning on line 5231 in packages/adapter/src/lib/adapter/adapter.ts

View workflow job for this annotation

GitHub Actions / Eslint

Missing JSDoc @param "_native" description
* @param options
* @param callback
* @deprecated use `this.extendObject` instead
Expand Down
20 changes: 19 additions & 1 deletion packages/adapter/src/lib/adapter/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
encrypt,
decrypt,
appNameLowerCase,
getRootDir
getRootDir,
execAsync
} from '@iobroker/js-controller-common-db/tools';
import { SUPPORTED_FEATURES, type SupportedFeature } from '@/lib/adapter/constants.js';
import path from 'node:path';
Expand Down Expand Up @@ -125,3 +126,20 @@ export async function listInstalledNodeModules(namespace: string): Promise<strin

return dependencies;
}

/**
* Request a module name by given url using `npm view`
*
* @param url the url to the package which should be installed via npm
*/
export async function requestModuleNameByUrl(url: string): Promise<string> {
const res = await execAsync(`npm view ${url} name`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any need to set a cwd?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, not using any locally installed stuff


if (typeof res.stdout !== 'string') {
throw new Error(
`Could not determine module name for url "${url}". Unexpected stdout: "${res.stdout ? res.stdout.toString() : ''}"`
);
}

return res.stdout.trim();
}
Loading