enuchi / React-Google-Apps-Script

This is your boilerplate project for developing React apps inside Google Sheets, Docs, Forms and Slides projects. It's perfect for personal projects and for publishing complex add-ons in the Google Workspace Marketplace.
MIT License
1.33k stars 178 forks source link

How to create custom functions in V3 version? #222

Open kulovecc opened 5 months ago

kulovecc commented 5 months ago

/**

This is how it was created in previous versions, but it doesn't work in V3.

enuchi commented 5 months ago

Correct. I don't yet have a way to support this in V3.

You could look at the vite config file if you want to tinker with it, but right now you can either add the docs manually after the build, or use V2.

enuchi commented 5 months ago

https://github.com/enuchi/React-Google-Apps-Script/blob/main/vite.config.ts#L138-L141

enuchi commented 5 months ago

You can modify the config to get it working like this:

Install gas-entry-generator: yarn add -D gas-entry-generator

Add your custom function code in the format below:

// custom-functions.ts

export const MYCUSTOMFUNCTION1 = (a: number, b: number) => a * b;
export const MYCUSTOMFUNCTION2 = (a: number, b: number) => a ^ b;

/**
 * Returns the product of the two numbers
 * @param {number} a The first value to multiply.
 * @param {number} b The second value to multiply.
 * @returns The product of the two numbers.
 * @customfunction
 * @preserve
 */
// @ts-expect-error
exports.MYCUSTOMFUNCTION1 = (a: number, b: number) => MYCUSTOMFUNCTION1(a, b);

/**
 * Returns a to the power of b
 * @param {number} a The base number.
 * @param {number} b The number to raise a to.
 * @returns The number a to the power of b.
 * @customfunction
 * @preserve
 */
// @ts-expect-error
exports.MYCUSTOMFUNCTION2 = (a: number, b: number) => MYCUSTOMFUNCTION2(a, b);

Export in main server entrypoint:

// index.ts
...
export { MYCUSTOMFUNCTION1, MYCUSTOMFUNCTION2 } from './custom-functions';

Modify the serverBuildConfig's rollupOptions.output section in vite.config.ts:

// vite.config.ts
...
import { generate } from 'gas-entry-generator';
...
rollupOptions: {
    output: {
      entryFileNames: 'code.js',
      extend: true,
      footer: (chunk: RenderedChunk) => {
        const fullCode = chunk.moduleIds
          .map((moduleId) => chunk.modules[moduleId].code)
          .join('\n');

        const options = {
          comment: true,
          autoGlobalExports: true,
        };
        const output = generate(fullCode, options);
        const entryPointFunctionSnippet = output.entryPointFunctions;
        const globalAssignedFunctionNames = new Set([]);

        const functionNameRegex = /function (\w+)/g;
        let match = functionNameRegex.exec(entryPointFunctionSnippet);
        while (match !== null) {
          globalAssignedFunctionNames.add(match[1]);
          match = functionNameRegex.exec(entryPointFunctionSnippet);
        }
        return (
          chunk.exports
            .filter(
              (exportedFunction) =>
                !globalAssignedFunctionNames.has(exportedFunction)
            )
            .map((exportedFunction) => `function ${exportedFunction}() {};`)
            .join('\n') + output.entryPointFunctions
        );
      },
    },
  }
...

When you run yarn deploy it should show autocomplete options.

kulovecc commented 4 months ago

Thank you for sharing this solution!

I have a question regarding the generate method used in the vite.config.ts configuration. You mentioned:

const output = generate(fullCode, options);

However, I couldn't find any references to this generate method. Could you please provide more details on what this method is, where it comes from, or how it should be implemented?

Thank you in advance for your help!

enuchi commented 4 months ago

Right, sorry, I forgot to add the import statement for the top of vite.config.ts:

import { generate } from 'gas-entry-generator';

I'll update the comment above.