0

Consider a following simple example, file country.gs

class Country { }

and file file subcountry.gs

class SubCountry extends Country{ }

function test(){}

Trying to run test() I get

ReferenceError: Country is not defined

If I join files or change loading order, it works fine.

Apparently, I don't want to be dependent on file load order, also clasp changes on push(sorting alphabetically), so it's definitely not a good way to rename files in order they should be compiled.

Is there an appropriate solution for this?

Example:

https://script.google.com/d/1Pipt3YN1FBGkbRRT2PyCHhugd-Xrv3zctIWYwX-cGnAjXfDckwOk7bJh/edit?usp=sharing

roma
  • 1,512
  • 10
  • 20

3 Answers3

5

As written in the documentation,

This arrangement is identical to how browsers handle multiple tags in one HTML file.

Each file is like a new <script>file content </script> tag and they're added in the order they appear in Apps script editor. This is a problem only when you're using global variables. It's explicitly discouraged to use global variables.

Caution: It's not best practice to rely on a specific file parse order to avoid this issue. The sequence of script file parsing can change if script files are copied, removed, renamed, or otherwise rearranged. It's better to remove any global variable dependency on function calls if possible.

Classes are infact "special functions". You can always enclose the Class in a local scope and call, when needed as recommended in the documentation.

Snippet:

Just moving the calling function to local scope should work

/*subcountry.gs*/

function test(){
  /*local scope*/class SubCountry extends Country{ }
}

To avoid declaring class in global scope as well:

/*country.gs*/
var Country;

function main(){
  if (Country == undefined) Country = class Country { }
  return Country;
}
/*subcountry.gs*/

function test(){
  /*initialize class Country*/main()
  /*local scope*/class SubCountry extends Country{ }
}
TheMaster
  • 45,448
  • 6
  • 62
  • 85
  • 1
    Would you be able to expand your answer to include how the OP's code could be written to follow your recommendation of avoiding global scope declarations? – Diego Jan 10 '21 at 01:26
3

Building off the answer posted by TheMaster and the Bruce Mcpherson article shared by Alan Wells, you could try implementing your own require() function.

/* Code.gs */
function test() {
  const SubCountry = require("SubCountry");
  const x = new SubCountry();
}
/* SubCountry.gs */
function SubCountry() {
  const Country = require("Country");
  return class SubCountry extends Country {};
}
/* Country.gs */
function Country() {
  return class Country {};
}
/* Require.gs */
function require(moduleName) {
  const modules = {
    Country: Country,
    SubCountry: SubCountry,
  };
  
  return modules[moduleName]();
}

Alternatively, you could apply a more direct approach without the use of require(), but I find this to be slightly less intuitive.

/* Code.gs */
function test() {
  const x = new (SubCountryClass())();
}
/* SubCountry.gs */
function SubCountryClass() {
  return class SubCountry extends CountryClass() {};
}
/* Country.gs */
function CountryClass() {
  return class Country {};
}

All files above, for both approaches, are intentionally presented and loaded in an order that would cause a ReferenceError if declaring the classes globally. So this should be fully independent of load order.

Diego
  • 9,261
  • 2
  • 19
  • 33
  • Thanks, this is very interesting wrap of Masters example. Might require some refactoring from me, but really explicit, so I might try it. Besides anyway I have to maintain full module list for exporting in other projects – roma Jan 11 '21 at 08:01
1

I'll probably go with one of solutions described here

TypeScript classes order in Google AppScript Project

using clasp and it's filePushOrder option

{
  "scriptId":"1Pipt3YN1FBGkbRRT2PyCHhugd-Xrv3zctIWYwX-cGnAjXfDckwOk7bJh",
  "filePushOrder": [
    "country.gs",
    "subcountry.gs"
  ]
}

Author example https://github.com/PopGoesTheWza/clasp-filePushOrder

I enforces me to use clasp, but at least it's easy to maintain.

roma
  • 1,512
  • 10
  • 20