@datalackey/gas-demodulify-plugin 0.1.0

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.
Files changed (29) hide show
  1. package/README.md +605 -0
  2. package/dist/index.d.ts +42 -0
  3. package/dist/index.js +45 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/plugin/GASDemodulifyPlugin.d.ts +33 -0
  6. package/dist/plugin/GASDemodulifyPlugin.js +125 -0
  7. package/dist/plugin/GASDemodulifyPlugin.js.map +1 -0
  8. package/dist/plugin/Logger.d.ts +22 -0
  9. package/dist/plugin/Logger.js +64 -0
  10. package/dist/plugin/Logger.js.map +1 -0
  11. package/dist/plugin/code-emission/CodeEmitter.d.ts +14 -0
  12. package/dist/plugin/code-emission/CodeEmitter.js +253 -0
  13. package/dist/plugin/code-emission/CodeEmitter.js.map +1 -0
  14. package/dist/plugin/code-emission/shared-types.d.ts +46 -0
  15. package/dist/plugin/code-emission/shared-types.js +3 -0
  16. package/dist/plugin/code-emission/shared-types.js.map +1 -0
  17. package/dist/plugin/code-emission/wildcards-resolution-helpers.d.ts +7 -0
  18. package/dist/plugin/code-emission/wildcards-resolution-helpers.js +100 -0
  19. package/dist/plugin/code-emission/wildcards-resolution-helpers.js.map +1 -0
  20. package/dist/plugin/invariants.d.ts +13 -0
  21. package/dist/plugin/invariants.js +23 -0
  22. package/dist/plugin/invariants.js.map +1 -0
  23. package/dist/plugin/plugin-configuration-options/options.schema.d.ts +41 -0
  24. package/dist/plugin/plugin-configuration-options/options.schema.js +67 -0
  25. package/dist/plugin/plugin-configuration-options/options.schema.js.map +1 -0
  26. package/dist/plugin/plugin-configuration-options/validateAndNormalizePluginOptions.d.ts +5 -0
  27. package/dist/plugin/plugin-configuration-options/validateAndNormalizePluginOptions.js +12 -0
  28. package/dist/plugin/plugin-configuration-options/validateAndNormalizePluginOptions.js.map +1 -0
  29. package/package.json +51 -0
package/README.md ADDED
@@ -0,0 +1,605 @@
1
+ # gas-demodulify
2
+
3
+ ## Table of Contents
4
+
5
+ <!-- TOC:START -->
6
+ - [gas-demodulify](#gas-demodulify)
7
+ - [Table of Contents](#table-of-contents)
8
+ - [Plugin Overview](#plugin-overview)
9
+ - [Support for Modern Architectures Comprised of Subsystems](#support-for-modern-architectures-comprised-of-subsystems)
10
+ - [UI subsystem](#ui-subsystem)
11
+ - [Backend (GAS) subsystem](#backend-gas-subsystem)
12
+ - [Common subsystem](#common-subsystem)
13
+ - [Example](#example)
14
+ - [Backend subsystem (`gas/`)](#backend-subsystem-gas)
15
+ - [Common subsystem (`common/`)](#common-subsystem-common)
16
+ - [UI subsystem (`ui/`)](#ui-subsystem-ui)
17
+ - [What the Plugin Generates](#what-the-plugin-generates)
18
+ - [1. Backend bundle (`backend.gs`)](#1-backend-bundle-backendgs)
19
+ - [2. Common subsystem bundles](#2-common-subsystem-bundles)
20
+ - [COMMON for backend (`common.gs`)](#common-for-backend-commongs)
21
+ - [COMMON for UI (`common.html`)](#common-for-ui-commonhtml)
22
+ - [3. UI bundle (`ui.html`)](#3-ui-bundle-uihtml)
23
+ - [Finer Points Regarding How Code Must Be Bundled for GAS](#finer-points-regarding-how-code-must-be-bundled-for-gas)
24
+ - [Why should client-side browser code be processed with Webpack at all?](#why-should-client-side-browser-code-be-processed-with-webpack-at-all)
25
+ - [How Load Order Can Be Leveraged to Manage Inter-Subsystem Dependencies -- OBSOLETE](#how-load-order-can-be-leveraged-to-manage-inter-subsystem-dependencies----obsolete)
26
+ - [GAS Load Order Constraints](#gas-load-order-constraints)
27
+ - [Restrictions](#restrictions)
28
+ - [Configuration](#configuration)
29
+ - [General Options](#general-options)
30
+ - [module.exports.entry](#moduleexportsentry)
31
+ - [Plugin Constructor Options](#plugin-constructor-options)
32
+ - [*namespaceRoot*](#namespaceroot)
33
+ - [*subsystem*](#subsystem)
34
+ - [*buildMode*](#buildmode)
35
+ - [*defaultExportName*](#defaultexportname)
36
+ - [Example](#example-1)
37
+ - [Log level](#log-level)
38
+ - [Of Interest to Contributors](#of-interest-to-contributors)
39
+ <!-- TOC:END -->
40
+
41
+
42
+
43
+ ## Plugin Overview
44
+
45
+ A Webpack plugin that flattens modular TypeScript codebases into
46
+ [Google Apps Script](https://workspace.google.com/products/apps-script/) (GAS)-safe
47
+ JavaScript with clean **hierarchical
48
+ namespaces** corresponding to the top-level subsystems of a complex [GAS
49
+ add-on extension](https://developers.google.com/apps-script/guides/sheets).
50
+ This plugin was originally intended to serve as the core of an opinionated build
51
+ system for such extensions. Most existing Webpack-based tooling and GAS starter repos deal with
52
+ simple codebases and flat scripts, but fail when applied to more
53
+ complex architectures.
54
+
55
+ So, if your (Typescript) code base
56
+ - has multiple subsystems, and
57
+ - you want your emitted GAS code to isolate code for each subsystem into its own namespace, and
58
+ - you are horrified at the prospect of using brittle search and replace on strings to post-modify webpack output
59
+
60
+ then this plugin is for you.
61
+
62
+
63
+ When generating code `gas-demodulify` completely discards Webpack’s emitted runtime artifacts
64
+ — including the __webpack_require__
65
+ mechanism and its wrapping [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE).
66
+ Instead, it generates fresh, GAS-safe JavaScript
67
+ compatible with both the GAS runtime and
68
+ the [HtmlService](https://developers.google.com/apps-script/reference/html/html-service)
69
+ delivery model using:
70
+
71
+ - user supplied namespace configuration metadata
72
+ - transpiled module sources provided by Webpack’s compilation pipeline
73
+ (after module dependency resolution and type-checking, but before runtime execution)
74
+
75
+ Caveat: Our plugin disallows certain patterns and configurations -- in both source code and Webpack config --
76
+ that produce invalid GAS code. See the [Restrictions](#restrictions) section for details.
77
+
78
+
79
+
80
+ ## Support for Modern Architectures Comprised of Subsystems
81
+
82
+ A modern architecture for a complex GAS add-on typically comprises subsystems, with a common organization
83
+ breaking down into: **ui**, **backend (gas)**, and **common**. We will describe the operation of the plugin assuming
84
+ this tri-layer organization, but the plugin can be adapted to other architectures as well,
85
+ as discussed in the configuration section, below.
86
+
87
+ ### UI subsystem
88
+
89
+ The UI subsystem typically consists of:
90
+
91
+ - HTMLService dialogs &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # (not typically bundled, but pushed 'raw' by clasp)
92
+ - Sidebar interfaces &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # (not typically bundled, but pushed 'raw' by clasp)
93
+ - svg images for icons &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # (not typically bundled, but pushed 'raw' by clasp)
94
+ - Client-side controller logic running in browser
95
+ - Multi-step orchestration flows unsuitable for pure GAS execution
96
+
97
+ ### Backend (GAS) subsystem
98
+
99
+ This subsystem contains:
100
+
101
+ - Apps Script entrypoint functions invoked from the UI
102
+ - Spreadsheet/Drive API logic
103
+ - Custom menu handlers
104
+ - Trigger functions (`onOpen`, `onEdit`, ...)
105
+ - Business logic executed on Google’s servers
106
+
107
+ ### Common subsystem
108
+
109
+ This subsystem hosts shared utility code that must exist in **both** UI and backend bundles:
110
+
111
+ - Logging support
112
+ - Data models
113
+ - Any reusable logic shared across UI and backend
114
+
115
+ This tri-layer architecture reflects the natural separation required by
116
+ Apps Script: `ui` code runs in a browser iframe, `backend` code runs in the
117
+ GAS runtime, and `common` code must be bundled twice. The double bundling of `common` code
118
+ is necessary because we need to make common code available in two different generated code artifacts. One is to
119
+ be included in the HTML served to
120
+ the client via
121
+ [HtmlService.createHtmlOutputFromFile](https://developers.google.com/apps-script/reference/html/html-service#createHtmlOutputFromFile(String)),
122
+ and this requires the code live in a file with extension '.html'. The other is to
123
+ be included in the server-side GAS code, and thus must have extension '.gs'.
124
+
125
+
126
+ A normal Webpack build cannot satisfy the above requirements, as well as some of the more subtle
127
+ requirements discussed in [this section](#finer-points-regarding-how-code-must-be-bundled-for-gas)
128
+ .
129
+ Furthermore, Google Apps Script cannot:
130
+ - run Webpack's module runtime, `__webpack_require__`,
131
+ - nor its wrapping IIFE,
132
+ - nor resolve its internal module map.
133
+
134
+ Modern TypeScript/ESM code must therefore be **demodulified** — stripped of all the webpack require stuff,
135
+ and flattened into plain top-level functions.
136
+
137
+ **gas-demodulify** performs exactly this transformation.
138
+
139
+ ------------------------------------------------------------------------
140
+
141
+ ### Example
142
+
143
+ Suppose you are developing a Google Sheets add-on named **MyAddon**.
144
+
145
+ Assume your subsystems import export the following:
146
+
147
+ ### Backend subsystem (`gas/`)
148
+
149
+ import { Logger } from '../common/logger';
150
+
151
+ export function getData() {
152
+ Logger.log('getData called');
153
+ return "backend-data";
154
+ }
155
+
156
+ ### Common subsystem (`common/`)
157
+
158
+ export class Logger {
159
+ static log(msg: string) {
160
+ console.log(`LOG: ${msg}`);
161
+ }
162
+ }
163
+
164
+ ### UI subsystem (`ui/`)
165
+
166
+ import { Logger } from '../common/logger';
167
+
168
+ export function startUiFlow() {
169
+ Logger.log("UI flow started");
170
+ google.script.run
171
+ .withSuccessHandler(result => Logger.log(`Backend returned: ${result}`))
172
+ .getData(); // must be invoked via google.script.run
173
+ }
174
+
175
+ ------------------------------------------------------------------------
176
+
177
+ ## What the Plugin Generates
178
+
179
+ ### 1. Backend bundle (`backend.gs`)
180
+
181
+ // Namespace initialization
182
+ (function init(ns) {
183
+ let o = globalThis;
184
+ for (const p of ns.split(".")) o = o[p] = o[p] || {};
185
+ })("MYADDON.GAS");
186
+
187
+
188
+ Logger = MYADDON.COMMON.Logger.
189
+
190
+ // Flattened backend code
191
+ function getData() {
192
+ Logger.log("getData called");
193
+ return "backend-data";
194
+ }
195
+
196
+ // Export surface
197
+ globalThis.MYADDON.GAS.getData = getData;
198
+
199
+ ### 2. Common subsystem bundles
200
+
201
+ The COMMON bundle is emitted **twice**, once for backend and once for
202
+ UI.
203
+
204
+ #### COMMON for backend (`common.gs`)
205
+
206
+ (function init(ns) {
207
+ let o = globalThis;
208
+ for (const p of ns.split(".")) o = o[p] = o[p] || {};
209
+ })("MYADDON.GAS");
210
+
211
+ // Imported symbol bindings
212
+ const Logger = MYADDON.COMMON.Logger;
213
+
214
+ class Logger {
215
+ static log(msg) {
216
+ console.log(`LOG: ${msg}`);
217
+ }
218
+ }
219
+
220
+ globalThis.MYADDON.GAS.Logger = Logger;
221
+
222
+ #### COMMON for UI (`common.html`)
223
+
224
+ <script>
225
+ // Namespace initialization
226
+ (function init(ns) {
227
+ let o = globalThis;
228
+ for (const p of ns.split(".")) o = o[p] = o[p] || {};
229
+ })("MYADDON.UI");
230
+
231
+ class Logger {
232
+ static log(msg) {
233
+ console.log(`LOG: ${msg}`);
234
+ }
235
+ }
236
+
237
+ globalThis.MYADDON.UI.Logger = Logger;
238
+ </script>
239
+
240
+ ### 3. UI bundle (`ui.html`)
241
+
242
+ <script>
243
+ // Namespace initialization
244
+ (function init(ns) {
245
+ let o = globalThis;
246
+ for (const p of ns.split(".")) o = o[p] = o[p] || {};
247
+ })("MYADDON.UI");
248
+
249
+ // Import bindings
250
+ const Logger = MYADDON.COMMON.Logger;
251
+
252
+ // UI function that uses COMMON and calls backend
253
+ function startUiFlow() {
254
+ MYADDON.COMMON.Logger.log("UI flow started");
255
+ google.script.run
256
+ .withSuccessHandler(result =>
257
+ MYADDON.COMMON.Logger.log(`Backend returned: ${result}`)
258
+ )
259
+ .getData();
260
+ }
261
+
262
+ // Export to namespace
263
+ globalThis.MYADDON.UI.startUiFlow = startUiFlow;
264
+ </script>
265
+
266
+
267
+
268
+ ------------------------------------------------------------------------
269
+
270
+ ## Finer Points Regarding How Code Must Be Bundled for GAS
271
+
272
+ ### Why should client-side browser code be processed with Webpack at all?
273
+
274
+
275
+ Although the UI code of a GAS add-on ultimately executes inside your
276
+ browser (for example, within a dialog or sidebar iframe),
277
+ the browser never receives your JavaScript as a distinct chunk separate from the surrounding mark-up.
278
+ All UI code must be delivered through GAS's
279
+ [HtmlService](https://developers.google.com/apps-script/reference/html/html-service),
280
+ which expects you to load exactly one HTML file,
281
+ with all JavaScript imports resolved and all code inlined and delivered
282
+ together with the HTML markup as a single unit.
283
+
284
+ In a conventional web application, client-side JavaScript may be split
285
+ across many files and loaded dynamically by the browser using ES modules.
286
+ For example, HTML such as:
287
+
288
+ ```html
289
+ <script type="module" src="./main.js"></script>
290
+ ```
291
+
292
+ instructs the browser to treat main.js as an ES module entrypoint. The
293
+ browser will then issue an HTTP request such as:
294
+
295
+ ```http
296
+ GET /main.js
297
+ ```
298
+
299
+ and, as additional import statements are encountered, will issue further
300
+ requests for each referenced module. These requests are resolved by a web
301
+ server that exposes URL-addressable JavaScript resources (for example,
302
+ static files served from the application’s document root).
303
+
304
+ Google Apps Script does not provide such a delivery model. HtmlService emits
305
+ a single, generated HTML document and does not expose a web server capable
306
+ of responding to follow-on requests for JavaScript modules. As a result,
307
+ there are no URL-addressable resources corresponding to ./main.js (or any
308
+ other imported module), and ES module loading via
309
+
310
+ ```html
311
+ <script type="module">
312
+ ```
313
+
314
+ is fundamentally unsupported in GAS. Therefore, even though the
315
+ browser environment itself is fully capable of executing ES, GAS cannot deliver ES modules.
316
+ For this reason, all UI code must be bundled
317
+ into a single, flat `<script>` block, with all imports resolved ahead of
318
+ time, no import or export syntax remaining, and no Webpack runtime
319
+ present. Webpack, in conjunction with our plugin, performs this 'flattening'
320
+ and demodulification automatically.
321
+
322
+
323
+
324
+
325
+ ### How Load Order Can Be Leveraged to Manage Inter-Subsystem Dependencies -- OBSOLETE
326
+
327
+ Most complex GAS add-ons begin with a tri-layer structure:
328
+
329
+ - `ui` (browser code)
330
+ - `gas` (backend server code)
331
+ - `common` (shared utilities)
332
+
333
+ But some grow to include additional layers, such as:
334
+
335
+ - `charts`
336
+ - `api`
337
+ - `models`
338
+ - `validation`
339
+ - `sheets`
340
+ - `forms`
341
+
342
+ Each of these may depend on others, and the load order of generated
343
+ `.gs` files becomes important.
344
+
345
+ #### GAS Load Order Constraints
346
+
347
+ Google Apps Script evaluates `.gs` files in **lexicographical
348
+ (alphabetical)** order at runtime. This ordering is not configurable. It
349
+ imposes the following rule:
350
+
351
+ > Any subsystem that provides shared utilities must be emitted
352
+ > **before** subsystems that depend on it.
353
+
354
+ Example: the backend (`GAS`) subsystem normally depends on `COMMON`.
355
+ Therefore:
356
+
357
+ - `COMMON` must appear **first** in `.gs` load order.
358
+ - `GAS` must appear **after COMMON**.
359
+
360
+
361
+ In older build systems, developers often handled this requirement using ad-hoc
362
+ post-processing scripts or manual renaming, commonly prefixing shared bundles
363
+ with names like `AAA_common.gs` to force correct load order.
364
+
365
+ Our plugin, together with standard webpack configuration options, eliminates
366
+ the need for such fragile post-processing by ensuring
367
+ that all generated bundles are clean, GAS-compatible artifacts whose load order
368
+ is determined entirely by standard Webpack configuration. In particular,
369
+ lexicographical load ordering is enforced by choosing appropriate values for
370
+ Webpack’s `output.filename`, allowing shared dependencies to sort before the
371
+ subsystems that depend on them. You still have to think about your inter-subsystem dependencies
372
+ and choose names accordingly, but you can handle this entirely via webpack configuration, with no
373
+ tedious post-processing required.
374
+
375
+ At a minimum: ensure that your `common` bundle is named
376
+ so that it sorts before any other `.gs` bundles that depend on it, using `output.filename`.
377
+ For example the configuration below would produce an output file named `00_common.[contenthash].gs`,
378
+ which sorts before `01_gas.[contenthash].gs` and `02_charts.[contenthash].gs` etc.
379
+
380
+ ```javascript
381
+
382
+ module.exports = {
383
+
384
+ ....
385
+
386
+ entry: {
387
+ common: "./src/common/index.ts",
388
+ }
389
+ ...
390
+
391
+ output: {
392
+ filename: "00_[name].[contenthash].gs"
393
+ }
394
+ ```
395
+
396
+
397
+ ------------------------------------------------------------------------
398
+
399
+ ## Restrictions
400
+
401
+ This plugin enforces a small set of source-level and build-time restrictions.
402
+ Please design your code to avoid the following patterns; violations will
403
+ either be rejected by the plugin at build time or code you want to keep will be stripped (which may
404
+ cause hard-to-diagnose bugs that change runtime behavior).
405
+
406
+
407
+
408
+ - Forbidden Webpack runtime artifacts
409
+ - Any substring matching the values in [FORBIDDEN_WEBPACK_RUNTIME_SUBSTRINGS](src/plugin/invariants.ts)
410
+ is not allowed in emitted output. Currently, this includes:
411
+ - `__webpack_` (any Webpack helper/runtime identifier)
412
+ - `.__esModule` (ES module interop artifact)
413
+ - Rationale: GAS cannot execute Webpack's module runtime (for example `__webpack_require__`) or interop boilerplate.
414
+ - Fix: Remove direct references to Webpack internals from your source.
415
+
416
+ - No wildcard re-exports
417
+ - Patterns rejected: `export * from './module'`, `export * as ns from './module'`, and bare `export *`.
418
+ - Rationale: wildcard re-exports create a non-deterministic export surface which cannot be
419
+ reliably flattened to a single GAS namespace.
420
+ - Fix: Replace wildcard re-exports with explicit, named re-exports, for example:
421
+ - Bad: `export * from './utils'`
422
+ - Good: `export { foo, bar } from './utils'`
423
+
424
+ - Avoid dynamic/conditional module loading patterns
425
+ - Patterns such as dynamic `import(...)`, `require()` with non-static arguments, or runtime code generation
426
+ that depends on bundler behavior are fragile and may not demodulify correctly.
427
+ For example: `const mod = require("./helpers/" + helperName);`
428
+ - Fix: Prefer static, unconditional imports/exports so Webpack can produce deterministic, statically-analyzable output.
429
+ Note there is no enforcement of this by the plugin, but such patterns may lead to runtime errors.
430
+
431
+ - Source files and source maps
432
+ - The plugin strips some runtime helpers and rewrites lines; try to preserve source maps
433
+ during your toolchain if you rely on debugging information. Avoid constructs that cause significant codegen wrapper insertion.
434
+
435
+ - Exactly one TypeScript entry module
436
+ - The plugin requires exactly one TypeScript-authored entry module per build. The entry module defines the
437
+ entire public API surface exposed to the Google Apps Script runtime. All GAS-visible functions must
438
+ be exported from this module, either directly or via explicit named re-exports. Other files may participate
439
+ freely in implementation via imports, but only the entry module’s exports are attached to the GAS namespace.
440
+ - Disallowed entry configurations
441
+ - The following are explicitly not supported, even though Webpack itself may allow them:
442
+ - Array-based entries, e.g.: `entry: { gas: ["./a.ts", "./b.ts"] }`
443
+ - Glob-based or auto-discovered entries, e.g.: ` entry: { gas: glob.sync("src/gas/*.ts") }`
444
+ - See [here](docs/plugin-design.md#why-exactly-one-webpack-entry-is-required) for more details.
445
+
446
+ - Output filename is intentionally ignored
447
+ - When gas-demodulify is enabled, we ignore, and actually delete the JavaScript bundle that
448
+ Webpack would otherwise emit. This is because that bundle contains runtime artifacts that GAS
449
+ cannot execute. To make this obvious and avoid accidental misuse, the plugin requires a sentinel value
450
+ be specified for `output.filename` in your Webpack config: `output: { filename: "OUTPUT-BUNDLE-FILENAME-DERIVED-FROM-ENTRY-NAME" ...`
451
+ Any other value — including omitting `output.filename` — is rejected.
452
+ - See [here](docs/plugin-design.md#how-gas-demodulify-separates-wheat-application-code-from-chaff-webpack-boilerplate)
453
+ for more details.
454
+
455
+ - No aliased re-exports in the entry module
456
+ - Patterns rejected: `export { foo as bar } from './module'`
457
+ - Rationale:
458
+ - Re-exporting with an alias does **not** create a runtime identifier named `bar`
459
+ - Webpack erases alias intent during module graph construction
460
+ - gas-demodulify operates after this erasure and cannot safely recover the original binding
461
+ - Fix: Replace aliased re-exports with an explicit wrapper export in the entry module:
462
+ - Bad:
463
+ ```ts
464
+ export { onOpen as handleOpen } from "./triggers";
465
+ ```
466
+ - Good:
467
+ ```ts
468
+ import { onOpen } from "./triggers";
469
+ export function handleOpen() {
470
+ return onOpen();
471
+ }
472
+ ```
473
+
474
+
475
+
476
+ ## Configuration
477
+
478
+
479
+ ### General Options
480
+
481
+
482
+ #### module.exports.entry
483
+
484
+ The emitted output filename is derived from the Webpack entrypoint name.
485
+ For example, an entry named gas will emit gas.gs. This was discussed in more detail in the previous section's
486
+ discusion of the restriction *Exactly one TypeScript entry module*.
487
+
488
+
489
+
490
+ ### Plugin Constructor Options
491
+
492
+ The code snippet below illustrates how to pass options to the GASDemodulifyPlugin constructor via
493
+ a standard Javascript dictionary:
494
+
495
+ > new GASDemodulifyPlugin({
496
+ > namespaceRoot: "MYADDON",
497
+ > subsystem: "GAS",
498
+ > buildMode: "gas",
499
+ > logLevel: "info"
500
+ > });
501
+
502
+
503
+ You can call the plugin with an empty options object, and all options will take their default values.
504
+
505
+
506
+ #### *namespaceRoot*
507
+
508
+ The top-level global namespace under which all generated symbols will be attached (e.g. MYADDON, MyCompany.ProjectFoo).
509
+ - Default: DEFAULT
510
+
511
+
512
+ #### *subsystem*
513
+ In most projects, this is a single identifier such as `UI`, `GAS`, or `COMMON`, and for the example above
514
+ we get the namespace: `MYADDON.UI` Advanced users may specify a dotted path to create deeper hierarchy:
515
+
516
+ namespaceRoot: "MYADDON"
517
+ subsystem: "UI.Dialogs"
518
+
519
+ Which produces `MYADDON.UI.Dialogs`
520
+ - Default: DEFAULT
521
+
522
+
523
+ #### *buildMode*
524
+
525
+ Controls which artifacts are emitted:
526
+
527
+ - "gas" → emits .gs
528
+ - "ui" → emits .html with inline script tags
529
+ - "common" → emits both .gs and .html
530
+
531
+ - Default: gas
532
+
533
+ #### *defaultExportName*
534
+
535
+ Controls how default exports are attached to the GAS namespace.
536
+ If this option is provided, the default export is mapped to the specified symbol name.
537
+
538
+ - Default: - defaultExport
539
+
540
+
541
+ ###### Example
542
+
543
+ Given the following source code:
544
+
545
+ > export default function foo() {}
546
+
547
+
548
+ If no defaultExportName is specified, the generated output will be:
549
+
550
+ > globalThis.MYADDON.UI.defaultExport = defaultExport;
551
+
552
+
553
+ If defaultExportName is specified:
554
+
555
+ > new GASDemodulifyPlugin({
556
+ > namespaceRoot: "MYADDON",
557
+ > subsystem: "UI",
558
+ > buildMode: "ui",
559
+ > defaultExportName: "main",
560
+ > logLevel: "info"
561
+ > });
562
+
563
+ In this case, the default export is attached to the GAS namespace using the explicitly provided name main.
564
+
565
+ > globalThis.MYADDON.UI.main = main;
566
+
567
+
568
+
569
+ ### Log level
570
+
571
+ Control the verbosity of the plugin's diagnostic output. Accepted values are:
572
+
573
+ - "silent" — no *info* or *debug* logging, only *warn* and *error*
574
+ - "info" — high-level lifecycle messages (default)
575
+ - "debug" — verbose internal diagnostics
576
+
577
+ Precedence and behavior:
578
+
579
+ - If the environment variable `LOGLEVEL` is present and set to a valid value, it overrides the explicit `logLevel`
580
+ option passed to the plugin. For example:
581
+ - `LOGLEVEL=debug npm run build` will enable debug output regardless of the
582
+ plugin config's `logLevel` option.
583
+ - `LOGLEVEL=silent npm run build` will surpress all output except for warnings and errors.
584
+ (useful for figuring out which tests in a suite failed without reams of log noise).
585
+ - If `LOGLEVEL` is not set, the plugin uses the explicit `logLevel` option when provided.
586
+ - If neither `LOGLEVEL` nor an explicit `logLevel` is provided, the default level is `info`.
587
+ - Invalid log level values (from the environment or the explicit option) are treated as configuration errors and
588
+ will cause the build to fail.
589
+
590
+ Tests may set `LOGLEVEL` in the environment or inject `logLevel` into fixture plugin instances.
591
+ The environment variable takes precedence.
592
+
593
+ - Default: info
594
+
595
+ ## Of Interest to Contributors
596
+
597
+ If you’re interested in the internal architecture of this plugin or in contributing to its development, see:
598
+
599
+ - [Guidance for Plugin Maintainers](docs/guidance-for-plugin-maintainers.md)
600
+ - [Plugin Design](docs/plugin-design.md)
601
+
602
+
603
+ The design discussion also includes a discussion of how webpack typically fits into build pipelines which target
604
+ GAS as an execution environment.
605
+
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Package entrypoint and CommonJS compatibility boundary.
3
+ *
4
+ * This file defines the *public API surface* of the package.
5
+ * It intentionally exports exactly one value for Webpack users: the gas-demodulify-plugin plugin constructor.
6
+ *
7
+ * Why this exists:
8
+ * - The package is effectively published as CommonJS due to a combination of factors:
9
+ *
10
+ * 1) TypeScript compilation target:
11
+ * - `tsconfig.json` specifies `"module": "commonjs"`,
12
+ * which determines the emitted JavaScript semantics
13
+ * (`module.exports`, `require()`).
14
+ *
15
+ * 2) Package entrypoint declaration:
16
+ * - `package.json` exposes `dist/index.js` via the `main` field,
17
+ * defining how Node and bundlers load the package.
18
+ *
19
+ * 3) Node module interpretation rules:
20
+ * - The absence of `"type": "module"` in `package.json` causes Node
21
+ * to interpret `.js` files as CommonJS by default.
22
+ *
23
+ * Together, these establish a CommonJS contract for consumers.
24
+ *
25
+ * Consumer expectations:
26
+ * - Webpack plugins are typically consumed via `require("package-name")` -- not ESM `import` syntax.
27
+ * - Consumers expect that call to return the constructor itself,
28
+ * not a `{ default: ... }` wrapper or a namespace object.
29
+ *
30
+ * Implementation details:
31
+ * - Uses TypeScript `export =` syntax so the compiled output is:
32
+ * `module.exports = PluginConstructor`
33
+ * - Users of plugin use `require()` import style intentionally to preserve CommonJS semantics.
34
+ *
35
+ * Note on linting:
36
+ * - `require()` usage in this file is deliberate and confined to this
37
+ * package boundary.
38
+ * - ESLint rules discouraging `require()` are intended for application code,
39
+ * not for package entrypoint shims.
40
+ */
41
+ import GASDemodulifyPlugin = require("./plugin/GASDemodulifyPlugin");
42
+ export = GASDemodulifyPlugin;
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ /**
3
+ * Package entrypoint and CommonJS compatibility boundary.
4
+ *
5
+ * This file defines the *public API surface* of the package.
6
+ * It intentionally exports exactly one value for Webpack users: the gas-demodulify-plugin plugin constructor.
7
+ *
8
+ * Why this exists:
9
+ * - The package is effectively published as CommonJS due to a combination of factors:
10
+ *
11
+ * 1) TypeScript compilation target:
12
+ * - `tsconfig.json` specifies `"module": "commonjs"`,
13
+ * which determines the emitted JavaScript semantics
14
+ * (`module.exports`, `require()`).
15
+ *
16
+ * 2) Package entrypoint declaration:
17
+ * - `package.json` exposes `dist/index.js` via the `main` field,
18
+ * defining how Node and bundlers load the package.
19
+ *
20
+ * 3) Node module interpretation rules:
21
+ * - The absence of `"type": "module"` in `package.json` causes Node
22
+ * to interpret `.js` files as CommonJS by default.
23
+ *
24
+ * Together, these establish a CommonJS contract for consumers.
25
+ *
26
+ * Consumer expectations:
27
+ * - Webpack plugins are typically consumed via `require("package-name")` -- not ESM `import` syntax.
28
+ * - Consumers expect that call to return the constructor itself,
29
+ * not a `{ default: ... }` wrapper or a namespace object.
30
+ *
31
+ * Implementation details:
32
+ * - Uses TypeScript `export =` syntax so the compiled output is:
33
+ * `module.exports = PluginConstructor`
34
+ * - Users of plugin use `require()` import style intentionally to preserve CommonJS semantics.
35
+ *
36
+ * Note on linting:
37
+ * - `require()` usage in this file is deliberate and confined to this
38
+ * package boundary.
39
+ * - ESLint rules discouraging `require()` are intended for application code,
40
+ * not for package entrypoint shims.
41
+ */
42
+ /* eslint-disable @typescript-eslint/no-require-imports */
43
+ const GASDemodulifyPlugin = require("./plugin/GASDemodulifyPlugin");
44
+ module.exports = GASDemodulifyPlugin;
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,0DAA0D;AAC1D,oEAAqE;AACrE,iBAAS,mBAAmB,CAAC"}