Skip to content

Commit 66ff6a9

Browse files
authored
feat: validate crawlee versions in Actor.init (#301)
* feat: validate crawlee versions in `Actor.init` The SDK depends on `@crawlee/core`, but if the user installs an incompatible version of crawlee, they might end up with two installations of the `@crawlee/core` which means multiple static registers like the one for the global configuration, which breaks the integration, since the SDK will only touch the global config it sees, but it's a different one than the explicitly installed package. Closes #237 * normalize paths and declare missing dependency * add more details to the error message * support pnpm and yarn pnp
1 parent f499ed2 commit 66ff6a9

File tree

4 files changed

+43
-2
lines changed

4 files changed

+43
-2
lines changed

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/apify/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@
6363
"@crawlee/types": "^3.9.0",
6464
"@crawlee/utils": "^3.9.0",
6565
"apify-client": "^2.9.0",
66+
"fs-extra": "^11.2.0",
6667
"ow": "^0.28.2",
6768
"semver": "^7.5.4",
6869
"tslib": "^2.6.2",
6970
"ws": "^7.5.9"
7071
}
71-
}
72+
}

packages/apify/src/actor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { KeyValueStore } from './key_value_store';
4141
import { PlatformEventManager } from './platform_event_manager';
4242
import type { ProxyConfigurationOptions } from './proxy_configuration';
4343
import { ProxyConfiguration } from './proxy_configuration';
44-
import { logSystemInfo, printOutdatedSdkWarning } from './utils';
44+
import { checkCrawleeVersion, logSystemInfo, printOutdatedSdkWarning } from './utils';
4545

4646
/**
4747
* `Actor` class serves as an alternative approach to the static helpers exported from the package. It allows to pass configuration
@@ -193,6 +193,7 @@ export class Actor<Data extends Dictionary = Dictionary> {
193193

194194
this.initialized = true;
195195

196+
checkCrawleeVersion();
196197
logSystemInfo();
197198
printOutdatedSdkWarning();
198199

packages/apify/src/utils.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { type } from 'node:os';
2+
import { normalize } from 'node:path';
23

34
import { APIFY_ENV_VARS } from '@apify/consts';
45
import log from '@apify/log';
56
// @ts-expect-error if we enable resolveJsonModule, we end up with `src` folder in `dist`
67
import { version as crawleeVersion } from '@crawlee/core/package.json';
78
// @ts-expect-error if we enable resolveJsonModule, we end up with `src` folder in `dist`
89
import { version as apifyClientVersion } from 'apify-client/package.json';
10+
import { pathExistsSync, readJSONSync } from 'fs-extra';
911
import semver from 'semver';
1012

1113
// @ts-expect-error if we enable resolveJsonModule, we end up with `src` folder in `dist`
@@ -25,6 +27,42 @@ export function logSystemInfo() {
2527
});
2628
}
2729

30+
/**
31+
* Logs info about system, node version and apify package version.
32+
* @internal
33+
*/
34+
export function checkCrawleeVersion() {
35+
const paths = [
36+
// when users install `crawlee` package, we need to check its core dependency
37+
normalize(`${process.cwd()}/node_modules/crawlee/node_modules/@crawlee/core/package.json`),
38+
// when users install `@crawlee/cheerio` or other crawler package, we need to check the dependency under basic crawler package
39+
normalize(`${process.cwd()}/node_modules/@crawlee/basic/node_modules/@crawlee/core/package.json`),
40+
// also check paths via `require.resolve` to support pnpm
41+
require.resolve('crawlee/package.json'),
42+
require.resolve('@crawlee/basic/package.json'),
43+
];
44+
45+
for (const path of paths) {
46+
if (!pathExistsSync(path)) {
47+
continue;
48+
}
49+
50+
let version;
51+
52+
try {
53+
version = readJSONSync(path).version;
54+
} catch {
55+
//
56+
}
57+
58+
if (version != null && version !== crawleeVersion) {
59+
const details = `User installed version (${version}) found in ${path}.\nSDK uses ${crawleeVersion} from ${require.resolve('@crawlee/core')}`;
60+
// eslint-disable-next-line max-len
61+
throw new Error(`Detected incompatible Crawlee version used by the SDK. User installed ${version} but the SDK uses ${crawleeVersion}.\n\n${details}`);
62+
}
63+
}
64+
}
65+
2866
/**
2967
* Prints a warning if this version of Apify SDK is outdated.
3068
* @ignore

0 commit comments

Comments
 (0)