From 7599d7f059f5ba9bf77aaf97364edc7c3219038f Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 9 Sep 2021 11:04:05 +1000 Subject: [PATCH 1/7] allow module import for Parse Cloud --- spec/CloudCode.spec.js | 15 +++++++++++++++ src/Options/Definitions.js | 5 +++++ src/Options/docs.js | 1 + src/Options/index.js | 2 ++ src/ParseServer.js | 6 +++++- 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 86a7627427..6c0df0b035 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -39,6 +39,21 @@ describe('Cloud Code', () => { }); }); + it('can throw with invalid cloud', async () => { + await expectAsync(reconfigureServer({ cloud: [] })).toBeRejectedWith( + "argument 'cloud' must either be a string or a function" + ); + }); + + it('can use import for cloud files', async () => { + await reconfigureServer({ + cloud: './spec/cloud/cloudCodeRelativeFile.js', + module: true, + }); + const result = await Parse.Cloud.run('cloudCodeInFile', {}); + expect(result).toBe('It is possible to define cloud code in a file.'); + }); + it('can create functions', done => { Parse.Cloud.define('hello', () => { return 'Hello world!'; diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 65799f8191..8b166b957c 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -260,6 +260,11 @@ module.exports.ParseServerOptions = { env: 'PARSE_SERVER_MIDDLEWARE', help: 'middleware for express server, can be string or function', }, + module: { + env: 'PARSE_SERVER_MODULE', + help: 'Whether cloud should load using `import` instead of `require`.', + action: parsers.booleanParser, + }, mountGraphQL: { env: 'PARSE_SERVER_MOUNT_GRAPHQL', help: 'Mounts the GraphQL endpoint', diff --git a/src/Options/docs.js b/src/Options/docs.js index 30b3aba1a0..983cfcef9c 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -49,6 +49,7 @@ * @property {Number|String} maxLogFiles Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null) * @property {String} maxUploadSize Max file size for uploads, defaults to 20mb * @property {Union} middleware middleware for express server, can be string or function + * @property {Boolean} module Whether cloud should load using `import` instead of `require`. * @property {Boolean} mountGraphQL Mounts the GraphQL endpoint * @property {String} mountPath Mount path for the server, defaults to /parse * @property {Boolean} mountPlayground Mounts the GraphQL Playground - never use this option in production diff --git a/src/Options/index.js b/src/Options/index.js index 5f3d9afa47..e502eadaee 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -70,6 +70,8 @@ export interface ParseServerOptions { databaseAdapter: ?Adapter; /* Full path to your cloud code main.js */ cloud: ?string; + /* Whether cloud should load using `import` instead of `require`. */ + module: ?boolean; /* A collection prefix for the classes :DEFAULT: '' */ collectionPrefix: ?string; diff --git a/src/ParseServer.js b/src/ParseServer.js index 43996ac751..feff41faf2 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -103,7 +103,11 @@ class ParseServer { if (typeof cloud === 'function') { cloud(Parse); } else if (typeof cloud === 'string') { - require(path.resolve(process.cwd(), cloud)); + if (this.config.module) { + import(path.resolve(process.cwd(), cloud)); + } else { + require(path.resolve(process.cwd(), cloud)); + } } else { throw "argument 'cloud' must either be a string or a function"; } From 169f314e8f703249d26baa8bf0539c200ac2ccf3 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 9 Sep 2021 11:23:46 +1000 Subject: [PATCH 2/7] Update .babelrc --- .babelrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.babelrc b/.babelrc index 9151969bde..a199154b82 100644 --- a/.babelrc +++ b/.babelrc @@ -7,7 +7,8 @@ ["@babel/preset-env", { "targets": { "node": "12" - } + }, + "exclude": ["proposal-dynamic-import"] }] ], "sourceMaps": "inline" From 87c55903b4f8e1b420297ce84efd367c3074a7d3 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 9 Sep 2021 11:33:22 +1000 Subject: [PATCH 3/7] catch esm error --- spec/CloudCode.spec.js | 9 --------- src/Options/Definitions.js | 5 ----- src/Options/docs.js | 1 - src/Options/index.js | 2 -- src/ParseServer.js | 10 +++++++--- 5 files changed, 7 insertions(+), 20 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 6c0df0b035..4ff0cbd42b 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -45,15 +45,6 @@ describe('Cloud Code', () => { ); }); - it('can use import for cloud files', async () => { - await reconfigureServer({ - cloud: './spec/cloud/cloudCodeRelativeFile.js', - module: true, - }); - const result = await Parse.Cloud.run('cloudCodeInFile', {}); - expect(result).toBe('It is possible to define cloud code in a file.'); - }); - it('can create functions', done => { Parse.Cloud.define('hello', () => { return 'Hello world!'; diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 8b166b957c..65799f8191 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -260,11 +260,6 @@ module.exports.ParseServerOptions = { env: 'PARSE_SERVER_MIDDLEWARE', help: 'middleware for express server, can be string or function', }, - module: { - env: 'PARSE_SERVER_MODULE', - help: 'Whether cloud should load using `import` instead of `require`.', - action: parsers.booleanParser, - }, mountGraphQL: { env: 'PARSE_SERVER_MOUNT_GRAPHQL', help: 'Mounts the GraphQL endpoint', diff --git a/src/Options/docs.js b/src/Options/docs.js index 983cfcef9c..30b3aba1a0 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -49,7 +49,6 @@ * @property {Number|String} maxLogFiles Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null) * @property {String} maxUploadSize Max file size for uploads, defaults to 20mb * @property {Union} middleware middleware for express server, can be string or function - * @property {Boolean} module Whether cloud should load using `import` instead of `require`. * @property {Boolean} mountGraphQL Mounts the GraphQL endpoint * @property {String} mountPath Mount path for the server, defaults to /parse * @property {Boolean} mountPlayground Mounts the GraphQL Playground - never use this option in production diff --git a/src/Options/index.js b/src/Options/index.js index e502eadaee..5f3d9afa47 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -70,8 +70,6 @@ export interface ParseServerOptions { databaseAdapter: ?Adapter; /* Full path to your cloud code main.js */ cloud: ?string; - /* Whether cloud should load using `import` instead of `require`. */ - module: ?boolean; /* A collection prefix for the classes :DEFAULT: '' */ collectionPrefix: ?string; diff --git a/src/ParseServer.js b/src/ParseServer.js index feff41faf2..d79561d571 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -103,10 +103,14 @@ class ParseServer { if (typeof cloud === 'function') { cloud(Parse); } else if (typeof cloud === 'string') { - if (this.config.module) { - import(path.resolve(process.cwd(), cloud)); - } else { + try { require(path.resolve(process.cwd(), cloud)); + } catch (e) { + if (e.code === 'ERR_REQUIRE_ESM') { + import(path.resolve(process.cwd(), cloud)); + } else { + throw e; + } } } else { throw "argument 'cloud' must either be a string or a function"; From ae9869efa75d9169a16cc36596bd09b0becbbf11 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 9 Sep 2021 11:44:33 +1000 Subject: [PATCH 4/7] Update ParseServer.js --- src/ParseServer.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ParseServer.js b/src/ParseServer.js index d79561d571..a8b0ad38cf 100644 --- a/src/ParseServer.js +++ b/src/ParseServer.js @@ -103,14 +103,10 @@ class ParseServer { if (typeof cloud === 'function') { cloud(Parse); } else if (typeof cloud === 'string') { - try { + if (process.env.npm_package_type === 'module') { + import(path.resolve(process.cwd(), cloud)); + } else { require(path.resolve(process.cwd(), cloud)); - } catch (e) { - if (e.code === 'ERR_REQUIRE_ESM') { - import(path.resolve(process.cwd(), cloud)); - } else { - throw e; - } } } else { throw "argument 'cloud' must either be a string or a function"; From 88bac371b684102668d3bc58f9b34a8a121379fc Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 9 Sep 2021 12:09:20 +1000 Subject: [PATCH 5/7] add tests --- spec/CloudCode.spec.js | 8 ++++++++ spec/cloud/cloudCodeModuleFile.js | 3 +++ 2 files changed, 11 insertions(+) create mode 100644 spec/cloud/cloudCodeModuleFile.js diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 4ff0cbd42b..3c36a7a35b 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -45,6 +45,14 @@ describe('Cloud Code', () => { ); }); + it('can load cloud code as a module', async () => { + process.env.npm_package_type = 'module'; + await reconfigureServer({ cloud: './spec/cloud/cloudCodeModuleFile.js' }); + const result = await Parse.Cloud.run('cloudCodeInFile'); + expect(result).toEqual('It is possible to define cloud code in a file.'); + delete process.env.npm_package_type; + }); + it('can create functions', done => { Parse.Cloud.define('hello', () => { return 'Hello world!'; diff --git a/spec/cloud/cloudCodeModuleFile.js b/spec/cloud/cloudCodeModuleFile.js new file mode 100644 index 0000000000..a62b4fcc24 --- /dev/null +++ b/spec/cloud/cloudCodeModuleFile.js @@ -0,0 +1,3 @@ +Parse.Cloud.define('cloudCodeInFile', () => { + return 'It is possible to define cloud code in a file.'; +}); From d84bde3b90fcadf5a3091553e49ee0c81d16d95b Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 9 Sep 2021 12:14:51 +1000 Subject: [PATCH 6/7] Update CHANGELOG.md --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e6723c7aa..1a52469231 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -151,6 +151,7 @@ ___ - Refactor: uniform issue templates across repos (Manuel Trezza) [#7528](https://github.com/parse-community/parse-server/pull/7528) - ci: bump ci environment (Manuel Trezza) [#7539](https://github.com/parse-community/parse-server/pull/7539) - CI now pushes docker images to Docker Hub (Corey Baker) [#7548](https://github.com/parse-community/parse-server/pull/7548) +- Allow cloud string for ES modules (Daniel Blyth) [#7560](https://github.com/parse-community/parse-server/pull/7560) ## 4.10.3 [Full Changelog](https://github.com/parse-community/parse-server/compare/4.10.2...4.10.3) @@ -177,15 +178,15 @@ ___ *Versions >4.5.2 and <4.10.0 are skipped.* -> ⚠️ A security incident caused a number of incorrect version tags to be pushed to the Parse Server repository. These version tags linked to a personal fork of a contributor who had write access to the repository. The code to which these tags linked has not been reviewed or approved by Parse Platform. Even though no releases were published with these incorrect versions, it was possible to define a Parse Server dependency that pointed to these version tags, for example if you defined this dependency: +> ⚠️ A security incident caused a number of incorrect version tags to be pushed to the Parse Server repository. These version tags linked to a personal fork of a contributor who had write access to the repository. The code to which these tags linked has not been reviewed or approved by Parse Platform. Even though no releases were published with these incorrect versions, it was possible to define a Parse Server dependency that pointed to these version tags, for example if you defined this dependency: > ```js > "parse-server": "git@github.com:parse-community/parse-server.git#4.9.3" > ``` -> +> > We have since deleted the incorrect version tags, but they may still show up if your personal fork on GitHub or locally. We do not know when these tags have been pushed to the Parse Server repository, but we first became aware of this issue on July 21, 2021. We are not aware of any malicious code or concerns related to privacy, security or legality (e.g. proprietary code). However, it has been reported that some functionality does not work as expected and the introduction of security vulnerabilities cannot be ruled out. > -> You may be also affected if you used the Bitnami image for Parse Server. Bitnami picked up the incorrect version tag `4.9.3` and published a new Bitnami image for Parse Server. -> +> You may be also affected if you used the Bitnami image for Parse Server. Bitnami picked up the incorrect version tag `4.9.3` and published a new Bitnami image for Parse Server. +> >**If you are using any of the affected versions, we urgently recommend to upgrade to version `4.10.0`.** ## 4.5.2 From 83069bc4929098257b4973870daa1cbdf269a295 Mon Sep 17 00:00:00 2001 From: dblythy Date: Thu, 9 Sep 2021 12:43:21 +1000 Subject: [PATCH 7/7] Update CloudCode.spec.js --- spec/CloudCode.spec.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spec/CloudCode.spec.js b/spec/CloudCode.spec.js index 3c36a7a35b..07d94a366f 100644 --- a/spec/CloudCode.spec.js +++ b/spec/CloudCode.spec.js @@ -39,12 +39,6 @@ describe('Cloud Code', () => { }); }); - it('can throw with invalid cloud', async () => { - await expectAsync(reconfigureServer({ cloud: [] })).toBeRejectedWith( - "argument 'cloud' must either be a string or a function" - ); - }); - it('can load cloud code as a module', async () => { process.env.npm_package_type = 'module'; await reconfigureServer({ cloud: './spec/cloud/cloudCodeModuleFile.js' });