@ms-cloudpack/package-hashes 0.5.23 → 0.5.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/getPackageHashEntries.d.ts +7 -4
- package/lib/getPackageHashEntries.d.ts.map +1 -1
- package/lib/getPackageHashEntries.js +13 -0
- package/lib/getPackageHashEntries.js.map +1 -1
- package/lib/getSourceHash.d.ts +0 -1
- package/lib/getSourceHash.d.ts.map +1 -1
- package/lib/getSourceHash.js +5 -0
- package/lib/getSourceHash.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CloudpackConfig, PackageDefinitionsCache } from '@ms-cloudpack/common-types';
|
|
1
|
+
import type { CloudpackConfig, PackageDefinitionsCache, PackageSettings } from '@ms-cloudpack/common-types';
|
|
2
2
|
import type { PackagePatches } from './types/PackagePatches.js';
|
|
3
3
|
/**
|
|
4
4
|
* @returns the entries that are used to calculate the hash of a package
|
|
@@ -14,14 +14,18 @@ export declare function getPackageHashEntries(params: {
|
|
|
14
14
|
patches: PackagePatches;
|
|
15
15
|
};
|
|
16
16
|
}): Promise<{
|
|
17
|
+
dependencies: Partial<Record<string, string>> | undefined;
|
|
17
18
|
name: string;
|
|
18
19
|
version: string;
|
|
19
20
|
filesHash: Record<string, string | null | undefined> | null | undefined;
|
|
20
21
|
patchFileContent: string | undefined;
|
|
21
22
|
packageSettings: {
|
|
22
|
-
userPackageSettings:
|
|
23
|
-
generatedPackageSettings:
|
|
23
|
+
userPackageSettings: PackageSettings | undefined;
|
|
24
|
+
generatedPackageSettings: PackageSettings | undefined;
|
|
24
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* @returns the entries that are used to calculate the hash of a package
|
|
28
|
+
*/
|
|
25
29
|
description?: string | undefined;
|
|
26
30
|
files?: string[] | undefined;
|
|
27
31
|
directories?: {
|
|
@@ -45,7 +49,6 @@ export declare function getPackageHashEntries(params: {
|
|
|
45
49
|
typesVersions?: Partial<Record<string, Partial<Record<string, string[]>>>> | undefined;
|
|
46
50
|
exports?: import("@ms-cloudpack/common-types").PackageJsonExports | undefined;
|
|
47
51
|
imports?: import("@ms-cloudpack/common-types").PackageJsonImports | undefined;
|
|
48
|
-
dependencies?: Partial<Record<string, string>> | undefined;
|
|
49
52
|
devDependencies?: Partial<Record<string, string>> | undefined;
|
|
50
53
|
peerDependencies?: Partial<Record<string, string>> | undefined;
|
|
51
54
|
peerDependenciesMeta?: Partial<Record<string, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getPackageHashEntries.d.ts","sourceRoot":"","sources":["../src/getPackageHashEntries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"getPackageHashEntries.d.ts","sourceRoot":"","sources":["../src/getPackageHashEntries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAI5G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,sBAAsB,EAAE,OAAO,CAAC;KACjC,CAAC;IACF,OAAO,EAAE;QACP,MAAM,EAAE,eAAe,CAAC;QACxB,QAAQ,EAAE,uBAAuB,CAAC;QAClC,OAAO,EAAE,cAAc,CAAC;KACzB,CAAC;CACH;;;;;;;;;;IAbD;;OAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DF"}
|
|
@@ -10,6 +10,7 @@ export async function getPackageHashEntries(params) {
|
|
|
10
10
|
const { packagePath, isSourceHashingEnabled } = options;
|
|
11
11
|
const { packages, config, patches } = context;
|
|
12
12
|
const definition = await packages.get(packagePath);
|
|
13
|
+
const disabledTransformsDefinition = await packages.get(packagePath, { disableTransforms: true });
|
|
13
14
|
if (!definition) {
|
|
14
15
|
throw new Error(`Package definition (package.json) missing or invalid at "${packagePath}"`);
|
|
15
16
|
}
|
|
@@ -22,10 +23,22 @@ export async function getPackageHashEntries(params) {
|
|
|
22
23
|
}
|
|
23
24
|
const isExternal = isExternalPackage(packagePath);
|
|
24
25
|
const packageSettings = getPackageSettings({ name, version, config });
|
|
26
|
+
// Homogenize the package settings to ensure that the hash is consistent even with empty settings.
|
|
27
|
+
packageSettings.userPackageSettings ??= {};
|
|
28
|
+
packageSettings.generatedPackageSettings ??= {};
|
|
29
|
+
// We can delete these properties because they affect Cloudpack internals,
|
|
30
|
+
// but not the reusability of the bundled package.
|
|
31
|
+
delete packageSettings.userPackageSettings.includedDependencies;
|
|
32
|
+
delete packageSettings.userPackageSettings.excludedDependencies;
|
|
33
|
+
delete packageSettings.generatedPackageSettings.includedDependencies;
|
|
34
|
+
delete packageSettings.generatedPackageSettings.excludedDependencies;
|
|
25
35
|
const filesHash = !isExternal && isSourceHashingEnabled ? await getSourceHash({ packagePath, packageSettings }) : undefined;
|
|
26
36
|
const patchFileContent = isExternal ? await getPatchFileContent(patches, name, version) : undefined;
|
|
27
37
|
return {
|
|
28
38
|
...definition,
|
|
39
|
+
// Use the dependencies from the disabled transforms definition to ensure that the hash is consistent,
|
|
40
|
+
// and unaffected by include and exclude changes.
|
|
41
|
+
dependencies: disabledTransformsDefinition?.dependencies,
|
|
29
42
|
name,
|
|
30
43
|
version: isExternal ? version : packagePath,
|
|
31
44
|
filesHash,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getPackageHashEntries.js","sourceRoot":"","sources":["../src/getPackageHashEntries.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAU3C;IACC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IACpC,MAAM,EAAE,WAAW,EAAE,sBAAsB,EAAE,GAAG,OAAO,CAAC;IACxD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAE9C,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"getPackageHashEntries.js","sourceRoot":"","sources":["../src/getPackageHashEntries.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAU3C;IACC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IACpC,MAAM,EAAE,WAAW,EAAE,sBAAsB,EAAE,GAAG,OAAO,CAAC;IACxD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAE9C,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACnD,MAAM,4BAA4B,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;IAElG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,4DAA4D,WAAW,GAAG,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;IACrC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,sDAAsD,WAAW,GAAG,CAAC,CAAC;IACxF,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,yDAAyD,WAAW,GAAG,CAAC,CAAC;IAC3F,CAAC;IAED,MAAM,UAAU,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAElD,MAAM,eAAe,GAAG,kBAAkB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IAEtE,kGAAkG;IAClG,eAAe,CAAC,mBAAmB,KAAK,EAAqB,CAAC;IAC9D,eAAe,CAAC,wBAAwB,KAAK,EAAqB,CAAC;IAEnE,0EAA0E;IAC1E,kDAAkD;IAClD,OAAO,eAAe,CAAC,mBAAmB,CAAC,oBAAoB,CAAC;IAChE,OAAO,eAAe,CAAC,mBAAmB,CAAC,oBAAoB,CAAC;IAChE,OAAO,eAAe,CAAC,wBAAwB,CAAC,oBAAoB,CAAC;IACrE,OAAO,eAAe,CAAC,wBAAwB,CAAC,oBAAoB,CAAC;IAErE,MAAM,SAAS,GACb,CAAC,UAAU,IAAI,sBAAsB,CAAC,CAAC,CAAC,MAAM,aAAa,CAAC,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE5G,MAAM,gBAAgB,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,mBAAmB,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpG,OAAO;QACL,GAAG,UAAU;QACb,sGAAsG;QACtG,iDAAiD;QACjD,YAAY,EAAE,4BAA4B,EAAE,YAAY;QACxD,IAAI;QACJ,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW;QAC3C,SAAS;QACT,gBAAgB;QAChB,eAAe;KAChB,CAAC;AACJ,CAAC","sourcesContent":["import type { CloudpackConfig, PackageDefinitionsCache, PackageSettings } from '@ms-cloudpack/common-types';\nimport { isExternalPackage } from '@ms-cloudpack/package-utilities';\nimport { getSourceHash } from './getSourceHash.js';\nimport { getPackageSettings } from '@ms-cloudpack/config';\nimport type { PackagePatches } from './types/PackagePatches.js';\nimport { getPatchFileContent } from './getPatchFileContent.js';\n\n/**\n * @returns the entries that are used to calculate the hash of a package\n */\nexport async function getPackageHashEntries(params: {\n options: {\n packagePath: string;\n isSourceHashingEnabled: boolean;\n };\n context: {\n config: CloudpackConfig;\n packages: PackageDefinitionsCache;\n patches: PackagePatches;\n };\n}) {\n const { options, context } = params;\n const { packagePath, isSourceHashingEnabled } = options;\n const { packages, config, patches } = context;\n\n const definition = await packages.get(packagePath);\n const disabledTransformsDefinition = await packages.get(packagePath, { disableTransforms: true });\n\n if (!definition) {\n throw new Error(`Package definition (package.json) missing or invalid at \"${packagePath}\"`);\n }\n\n const { name, version } = definition;\n if (!name) {\n throw new Error(`Package definition (package.json) missing name at \"${packagePath}\"`);\n }\n if (!version) {\n throw new Error(`Package definition (package.json) missing version at \"${packagePath}\"`);\n }\n\n const isExternal = isExternalPackage(packagePath);\n\n const packageSettings = getPackageSettings({ name, version, config });\n\n // Homogenize the package settings to ensure that the hash is consistent even with empty settings.\n packageSettings.userPackageSettings ??= {} as PackageSettings;\n packageSettings.generatedPackageSettings ??= {} as PackageSettings;\n\n // We can delete these properties because they affect Cloudpack internals,\n // but not the reusability of the bundled package.\n delete packageSettings.userPackageSettings.includedDependencies;\n delete packageSettings.userPackageSettings.excludedDependencies;\n delete packageSettings.generatedPackageSettings.includedDependencies;\n delete packageSettings.generatedPackageSettings.excludedDependencies;\n\n const filesHash =\n !isExternal && isSourceHashingEnabled ? await getSourceHash({ packagePath, packageSettings }) : undefined;\n\n const patchFileContent = isExternal ? await getPatchFileContent(patches, name, version) : undefined;\n\n return {\n ...definition,\n // Use the dependencies from the disabled transforms definition to ensure that the hash is consistent,\n // and unaffected by include and exclude changes.\n dependencies: disabledTransformsDefinition?.dependencies,\n name,\n version: isExternal ? version : packagePath,\n filesHash,\n patchFileContent,\n packageSettings,\n };\n}\n"]}
|
package/lib/getSourceHash.d.ts
CHANGED
|
@@ -9,7 +9,6 @@ export declare function getSourceHash(options: {
|
|
|
9
9
|
packagePath: string;
|
|
10
10
|
packageSettings?: {
|
|
11
11
|
userPackageSettings: PackageSettings | undefined;
|
|
12
|
-
generatedPackageSettings: PackageSettings | undefined;
|
|
13
12
|
};
|
|
14
13
|
}): Promise<Record<string, string | null | undefined> | null>;
|
|
15
14
|
//# sourceMappingURL=getSourceHash.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getSourceHash.d.ts","sourceRoot":"","sources":["../src/getSourceHash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"getSourceHash.d.ts","sourceRoot":"","sources":["../src/getSourceHash.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AASlE;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE;QAChB,mBAAmB,EAAE,eAAe,GAAG,SAAS,CAAC;KAClD,CAAC;CACH,6DA6BA"}
|
package/lib/getSourceHash.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appConfigFileName, generatedConfigFileName } from '@ms-cloudpack/config';
|
|
1
2
|
import glob from 'fast-glob';
|
|
2
3
|
import { existsSync } from 'fs';
|
|
3
4
|
import { hash } from 'glob-hasher';
|
|
@@ -27,6 +28,10 @@ export async function getSourceHash(options) {
|
|
|
27
28
|
}
|
|
28
29
|
const files = await glob(patterns, {
|
|
29
30
|
cwd: packagePath,
|
|
31
|
+
// We want to ignore the app config and generated config files because they are not part of the source code.
|
|
32
|
+
// Ignoring the generated config is especially important because it changes during init,
|
|
33
|
+
// and it would invalidate the cache for the entry package with the generated config.
|
|
34
|
+
ignore: [appConfigFileName, generatedConfigFileName],
|
|
30
35
|
});
|
|
31
36
|
const globHash = hash(files, { cwd: packagePath });
|
|
32
37
|
console.debug(`Hashed source files in ${packagePath} in ${performance.now() - start}ms`);
|
package/lib/getSourceHash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getSourceHash.js","sourceRoot":"","sources":["../src/getSourceHash.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"getSourceHash.js","sourceRoot":"","sources":["../src/getSourceHash.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAKnC;IACC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAEhC,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC;IACjD,MAAM,UAAU,GAAG,eAAe,EAAE,mBAAmB,EAAE,UAAU,CAAC;IACpE,oFAAoF;IACpF,+CAA+C;IAC/C,IAAI,QAAkB,CAAC;IACvB,IAAI,UAAU,EAAE,MAAM,EAAE,CAAC;QACvB,QAAQ,GAAG,UAAU,CAAC;IACxB,CAAC;SAAM,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;QACrD,QAAQ,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC;IACtD,CAAC;SAAM,CAAC;QACN,QAAQ,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE;QACjC,GAAG,EAAE,WAAW;QAChB,4GAA4G;QAC5G,wFAAwF;QACxF,qFAAqF;QACrF,MAAM,EAAE,CAAC,iBAAiB,EAAE,uBAAuB,CAAC;KACrD,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;IAEnD,OAAO,CAAC,KAAK,CAAC,0BAA0B,WAAW,OAAO,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC;IAEzF,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["import type { PackageSettings } from '@ms-cloudpack/common-types';\nimport { appConfigFileName, generatedConfigFileName } from '@ms-cloudpack/config';\nimport glob from 'fast-glob';\nimport { existsSync } from 'fs';\nimport { hash } from 'glob-hasher';\nimport path from 'path';\n\nconst excludeNodeModules = '!**/node_modules/**';\n\n/**\n * Get the hash of the source files in a package\n * If there is a src folder, hash the files in that folder, the .*json files and the inputPaths\n * If there are inputPaths, hash those files in addition to the .*json files\n * Otherwise, hash all files except node_modules\n */\nexport async function getSourceHash(options: {\n packagePath: string;\n packageSettings?: {\n userPackageSettings: PackageSettings | undefined;\n };\n}) {\n const start = performance.now();\n\n const { packagePath, packageSettings } = options;\n const inputPaths = packageSettings?.userPackageSettings?.inputPaths;\n // Use the inputPaths if they are defined, otherwise use the src folder if it exists\n // Otherwise, use all files except node_modules\n let patterns: string[];\n if (inputPaths?.length) {\n patterns = inputPaths;\n } else if (existsSync(path.join(packagePath, 'src'))) {\n patterns = ['src/**', '*.json', excludeNodeModules];\n } else {\n patterns = ['**', excludeNodeModules];\n }\n\n const files = await glob(patterns, {\n cwd: packagePath,\n // We want to ignore the app config and generated config files because they are not part of the source code.\n // Ignoring the generated config is especially important because it changes during init,\n // and it would invalidate the cache for the entry package with the generated config.\n ignore: [appConfigFileName, generatedConfigFileName],\n });\n\n const globHash = hash(files, { cwd: packagePath });\n\n console.debug(`Hashed source files in ${packagePath} in ${performance.now() - start}ms`);\n\n return globHash;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ms-cloudpack/package-hashes",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.24",
|
|
4
4
|
"description": "Utilities for hashing packages.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@ms-cloudpack/common-types": "^0.6.
|
|
18
|
-
"@ms-cloudpack/config": "^0.20.
|
|
19
|
-
"@ms-cloudpack/package-utilities": "^7.4.
|
|
17
|
+
"@ms-cloudpack/common-types": "^0.6.2",
|
|
18
|
+
"@ms-cloudpack/config": "^0.20.13",
|
|
19
|
+
"@ms-cloudpack/package-utilities": "^7.4.11",
|
|
20
20
|
"fast-glob": "^3.2.12",
|
|
21
21
|
"glob-hasher": "^1.4.2",
|
|
22
22
|
"object-hash": "^3.0.0"
|