This article will look at some of the opportunities you’ll have when you pull in your libraries inline rather than leaving them as references to external files. It’s a follow on from the articles on Pull libraries inline to your Apps Script project and Pulling in Apps Script libraries: how is it done which you may want to check out first.
If you are familiar with Node, you’ll know that modules consumed by your project that you include using yarn or npm, are pulled into the codebase of your project rather than being just a reference to some code in the cloud that could disappear at any time. These series of projects are about taking that approach, but without having to leave the Apps Script IDE to use some other tools.
I’ve already covered the basic methods and workflows in the previously referenced articles, but here’s some ideas for some more advanced opportunities.
Debugging and testing
I write a lot of libraries. That means I have to support and debug them, either without the debugger, or by creating test scripts in the library itself – which isn’t really testing it – so that I can use the debugger to set breakpoints in the library. None of that is ideal, especially when there are all sorts of different versions being used out there.
For most libraries I have a test project that can test their validity, but debugger support for libraries is limited, so I needed a better approach.
By inlining the libraries, and using the versionTreatment: “respect” option, I can recreate the exact environment for fixing reported issues, and use the debugger to set breakpoints in the library code. Since the library code is now temporarily part of my project, the debugger is fully available to figure out what is going on in the libraries, and to test fixes in the inlined copies.
Using the versionTreatment: “head” option, I can inline development versions of the libraries and do initial testing and debugging, again as part of my test project.
We can then commit these changes back to the library code, deploy new version(s), and remove the inlined versions from the test project with the revert() method
The inlined contains a number of options for inlining various versions using the versionTreatment options.
This describes which version of the referenced libraries to pull, and can take these values
respect – Respect the version mentioned in the manifest referencing the library. This is the default.
upgrade – Upgrade to the latest deployed version. Note that the deployment record for the new IDE is checked here. It doesn’t have access to libraries deployed using the old IDE, so if it can’t find a new IDE deployment it’ll fail with an error like this. If it does, then you could consider doing a sloppyupgrade (see later) as a workaround if you can’t deploy using the new IDE.Error: Couldn’t find any deployments for 1v_l4xN3ICa0lAW315NQEzAHPSoNiFdWHsMEwj2qA5t9cgZ5VWci2Qxv2:(cGoa) (upgrade:upgrade to the latest deployed versions) – maybe they were deployed with old IDE?
head – Use the latest code of the library whether or not it’s been deployed. This is equivalent to development mode. These will generate console messages like this.Modified reference to 1U6j9t_3ONTbhTCvhjwANMcEXeHXr4shgzTG0ZrRnDYLcFl3_IH2b2eAY:(cCacheHandler) from version 18 to head ((head:use the latest version whether or not deployed))
sloppyupgrade – The same as upgrade, but if it can find no deployed versions or the deployed version is lower than the one in the manifest it will do a head. This could generate a console message like thisSloppy upgrade of reference to library 1dajqLysdKo8IoqddtEaGhtUUlSbtSQ1Agi2K5cXSUm0DxXfLYouSO9yD:(bmRottler) from version 10 to head (use the latest deployed version if there is one – otherwise use the head)
Checking the latest versions
Using the “head” option to pull all the latest versions of all the libraries in the library tree into your test project is a good way to see if they all work with each other before deploying them as latest versions.
Checking for multiple versions
It’s very common for one library to be using version x of a library and a library further down the tree to be using version y, but you didn’t know it. That means that your project will be using different versions of the same library. Inlining all the libraries with the “respect” option will provide handy console messages about what it’s doing.
You can quickly see by inspecting the _bmlimport/__bmlimporter_gets script file whether you are referencing multiple versions. This example shows that this project references 2 different versions of cUseful library.
Perhaps you need that for some compatibility reason. bmLimporter supports multiple versions, so it will work.
More often than not though, you can harmonize the offending library version references to the same version.
When you include a library, it is initialized in the runtime specified in its own manifest. That means that project libraries may be running different environments. There are some issues with legacy code in V8, in particular with additional reserved words. If, for example, you have variable called ‘package’ in code that has been working fine for years, then you choose to use v8 it will fail.
Here’s a list of reserved words to avoid. Similarly, writing v8 specific code to a project that is using the legacy runtime will also fail.
Inlined libraries will all run in the single runtime of the test project, which of course may now throw up previously undetected incompatibility errors. You’ll see this error when you try to commit the inlined files as it originates from the Apps Script API disliking the code. Here’s where the API checks compatibility.
Always add the .throw() option when writing to the project unless you plan on handling the error yourself, but in any case, the api avoids damage by the detection of such errors before commiting.
Changing the runtimeVersion default
If you really want to stick with the legacy runtime for both your test project and all the inlined libararies, then use the runtimeVersion option like this.
The runtimeVersion option takes these values. bmLimporter will use ‘V8’ by default.
STABLEthe default Apps Script runtime (currently Rhino).
V8the V8 powered runtime.
DEPRECATED_ES5the Rhino runtime. Setting this value also prevents automatic migration to V8.
Classes in libraries are not directly visible to their host projects.
However, you can ask bmLimporter to export classes it finds in libraries too with the exportClasses option, used like this
You can then reference them in your host project like this
Although this might be handy, remember that if you revert to a library version you will lose access to the class definition.
You may want to revert to the library version of a project. During import, bmLimporter creates a file called __bmlimport/manifest.json. This is just a copy of the original manifest. Therefore it’s very straightforward to revert to the library version, simply by copying the contents into your appsscript.json, then deleting everything in __bmlimport/*.
The library also provides a .revert() method that does all that for you. Here’s an example of complete reverter script using the revert() method
To take that one stage further, you may want to simply refresh your project to inline different versions of the libraries. Perhaps because there’s a new deployment or you want to try a different versionTreatment.
You could do that with a revert() followed by a getInlineProjectFiles(), or you could use the refreshInlineProjectFiles() method.
Here’s an example of a refresher script.
Inlining libraries currently only supports server side Apps Script code in libraries – just like the library system in Apps Script. However you can use these techniques to create a library system for pulling in client side html too. This would give developers the chance to distribute and import ready made web app and add-on components.
You can already do this using Import, export and mix container bound and standalone Apps Script projects which has an example of inlining a complete Add-on.
However, if you combine multiple projects containing client side code and markup there might be global space collisions. Taking some manual action to avoid it would be hard to generalize. BmLimporter automates avoidance of collisions with server side library code, but it’s more complex with client side code.
I have some ideas I’m mulling over to overcome some of the gotchas – watch this space for future developments. Ping me with suggestions and offers of collaboration if you want to help to develop this project further.
Example converter, reverter and refresher
All are also on github in their respective repos at github.com/brucemcpherson