Kind of a long shot… but has anyone had success se...
# automation-api
w
Kind of a long shot… but has anyone had success serializing a React app (specifically React DOM’s Server module) to render a React tree within a GCP Cloud Function or AWS Lambda function. Never tried this, but was hoping to use React to power the templating of the HTTP Callback Response.
No luck. I tried using a dynamic import, as suggested here for both a React module, and a Vue module, but with no luck:
Copy code
Capturing modules can sometimes cause problems.
    Consider using import('./src/ProductCardVueApp/index.ts') or require('./src/ProductCardVueApp/index.ts') inside function 'renderProductCardReactApp': index.ts(21,29)
I did not try using a dynamic require
But the module was not captured and uploaded in the GCP Function’s source code
Copy code
module './src/ProductCardVueApp/index.ts' which indirectly referenced
            '(productCardData) => __awaiter(void  ...': index.ts(22,17): which captured
              variable 'renderer' which indirectly referenced
                function 'renderToString': build.dev.js(9156,44): which captured
                  variable 'templateRenderer' which indirectly referenced
                    function 'TemplateRenderer': build.dev.js(8872,49): which referenced
                      function 'createStream': build.dev.js(9090,64): which referenced
                        function 'TemplateStream': build.dev.js(8688,26): which referenced
                          function 'Transform': which referenced
                            function 'Duplex': which referenced
                              function 'Readable': which referenced
                                function 'ReadableState': which referenced
                                  function 'getHighWaterMark': which captured
                                    'NumberIsInteger', a function defined at
                                      function 'isInteger': which could not be serialized because
                                        it was a native code function.

    Function code:
      function isInteger() { [native code] }
Copy code
error: Error serializing '(req, res) => __awaiter(void 0, void ...': index.ts(25,220)

    '(req, res) => __awaiter(void 0, void ...': index.ts(25,220): captured
      variable 'renderer' which indirectly referenced
        function 'renderProductCardReactApp': index.tsx(21,29): which captured
          module './src/ProductCardReactApp/index.tsx' which indirectly referenced
            '(productCardData) => server_1.render ...': index.tsx(24,17): which captured
              module './src/ProductCardReactApp/index.tsx' which indirectly referenced
                '(_a) => { var props = __rest(_a, []) ...': index.tsx(20,30): which captured
                  module './src/ProductCardReactApp/ProductCard.tsx' which indirectly referenced
                    '({ buttons, description, percentOff, ...': ProductCard.tsx(25,22): which captured
                      variable 'React' which indirectly referenced
                        function 'createElementWithValidation': react.development.js(2182,36): which referenced
                          function 'validateChildKeys': react.development.js(2068,26): which referenced
                            function 'validateExplicitKey': react.development.js(2026,28): which referenced
                              function 'setCurrentlyValidatingElement$1': react.development.js(1944,40): which referenced
                                function 'describeUnknownElementTypeFrameInDEV': react.development.js(1828,45): which referenced
                                  function 'describeNativeComponentFrame': react.development.js(1665,37): which captured
                                    variable 'componentFrameCache' which indirectly referenced
                                      function 'get': which could not be serialized because
                                        it was a native code function.
^ Vue and React errors, specifically
I tried including the module explicitly, but apparently this is not yet supported for GCP:
Copy code
codePathOptions: {
        extraIncludePaths: [join(__dirname, '../../ProductCardReactApp')],
      },
Copy code
error: Error: codePathOptions.extraIncludePaths not currently supported in GCP.
I guess I could try a Lambda, but I was trying to do this all in GCP
p
Instead of using Pulumi to serialize the files, make an archive using the
Asset
resource and point it to your built app
Here’s an example:
Copy code
// Upload build output to the bucket as an alternative to
// > gsutil cp -z build/* {bucket}
globby.sync('**/*', { cwd: '../build' }).map(file => {
  const contentType = (x => (typeof x === 'string' ? x : undefined))(mime.contentType(file))
  const contentEncoding = contentType && compressible(contentType) ? 'gzip' : undefined
  const cacheControl =
    file === 'index.html'
      ? 'no-store'
      : file === 'service-worker.js'
      ? 'no-cache'
      : file.startsWith('static/')
      ? 'public, max-age=2592000, immutable'
      : undefined

  return new BucketObject(file, {
    name: file,
    bucket: bucket.name,
    source:
      contentType && compressible(contentType)
        ? new GzipFileAsset(`../build/${file}`)
        : new FileAsset(`../build/${file}`),
    contentType,
    contentEncoding,
    cacheControl,
  })
})
And here’s the implementation of `GzipFileAsset`:
Copy code
import util from 'util'
import stream from 'stream'
import { join } from 'path'
import { tmpdir } from 'os'
import { createGzip } from 'zlib'
import { createReadStream, createWriteStream } from 'fs'
import { FileAsset } from '@pulumi/pulumi/asset'

const pipeline = util.promisify(stream.pipeline)

export default class GzipFileAsset extends FileAsset {
  constructor(path: string) {
    const tempFile = `${[...Array(30)].map(() => Math.random().toString(36)[2]).join('')}.gz`
    const target = join(tmpdir(), tempFile)
    super(
      pipeline(createReadStream(path), createGzip(), createWriteStream(target)).then(() => target),
    )
  }
}
This can be simplified a bit, by the way
w
@prehistoric-account-60014 thank you. I wonder if this could be the start of an implementation for supporting GCP’s codePathOptions.extraIncludePaths
p
Perhaps, we’ve got some code that looks like this for simpler deployments
Copy code
const object = new gcp.storage.BucketObject('table-exports-function-source', {
  bucket: sourceBucket.name,
  source: new pulumi.asset.AssetArchive({
    lib: new pulumi.asset.FileArchive('./lib'),
    '.env': new pulumi.asset.FileAsset('./.env'),
    'package.json': new pulumi.asset.FileAsset('./package.json'),
    'yarn.lock': new pulumi.asset.FileAsset('./yarn.lock'),
  }),
})
🙌 1
In that case we just specify the few files and folders that matter as opposed to trying to emulate the
gsutil cp -z
utility
🙌 1
w
Thank you Miguel. I will let you know if I end up using this
👍 1