pulumi/pkg/codegen/nodejs/gen_lazyloads.go

128 lines
4.4 KiB
Go

// Copyright 2016-2022, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Large providers like azure-native generate code that has a high
// startup overhead in Node JS due to loading thousands of modules at
// once. The code in this file is dedicated to support on-demand
// (lazy) loading of modules at Node level to speed up program
// startup.
package nodejs
import (
"encoding/json"
"fmt"
"io"
"sort"
"strings"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
)
type lazyLoadGen struct{}
func newLazyLoadGen() *lazyLoadGen {
return &lazyLoadGen{}
}
// Generates TypeScript code to re-export a generated module. For
// resources and functions this is optimized to use lazy loading.
// Falls back to eager re-export for everything else.
func (ll *lazyLoadGen) genReexport(w io.Writer, exp fileInfo, importPath string) {
if exp.fileType == functionFileType {
// optimize lazy-loading function modules
ll.genFunctionReexport(w, exp.functionFileInfo, importPath)
} else if exp.fileType == resourceFileType {
// optimize lazy-loading resource modules
ll.genResourceReexport(w, exp.resourceFileInfo, importPath)
} else {
// non-optimized but foolproof eager reexport
fmt.Fprintf(w, "export * from %q;\n", importPath)
}
}
// Used as a follow up to multiple genReexport calls to generate a
// lazy-load polyfill to all the properties that need that.
func (*lazyLoadGen) genLazyLoads(w io.Writer, importPath string, properties ...string) {
sort.Strings(properties)
j, err := json.Marshal(properties)
contract.AssertNoErrorf(err, "error serializing properties")
fmt.Fprintf(w, "utilities.lazyLoad(exports, %s, () => require(%q));\n",
string(j), importPath)
}
// Generates TypeScript code that lazily imports and re-exports a
// module defining a resource, while also importing the resoure class
// in-scope. Needs to know which names the module defines
// (resourceFileInfo).
func (ll *lazyLoadGen) genResourceReexport(w io.Writer, i resourceFileInfo, importPath string) {
defer fmt.Fprintf(w, "\n")
quotedImport := fmt.Sprintf("%q", importPath)
// not sure how to lazy-load in presence of re-exported
// namespaces; bail and use an eager load in this case; also
// eager-import the class.
if i.methodsNamespaceName != "" {
fmt.Fprintf(w, "export * from %s;\n", quotedImport)
fmt.Fprintf(w, "import { %s } from %s;\n", i.resourceClassName, quotedImport)
return
}
// Re-export interfaces. This is type-only and does not
// generate a require() call.
fmt.Fprintf(w, "export { %s } from %s;\n",
strings.Join(i.interfaces(), ", "),
quotedImport)
// Re-export class type into the type group, see
// https://www.typescriptlang.org/docs/handbook/declaration-merging.html
fmt.Fprintf(w, "export type %[1]s = import(%[2]s).%[1]s;\n",
i.resourceClassName,
quotedImport)
// Mock re-export class value into the value group - for compilation.
fmt.Fprintf(w, "export const %[1]s: typeof import(%[2]s).%[1]s = null as any;\n",
i.resourceClassName,
quotedImport)
ll.genLazyLoads(w, importPath, i.resourceClassName)
}
// Generates TypeScript code that lazily imports and re-exports a
// module defining a function. Needs to which names the module defines
// (functionFileInfo).
func (ll *lazyLoadGen) genFunctionReexport(w io.Writer, i functionFileInfo, importPath string) {
defer fmt.Fprintf(w, "\n")
quotedImport := fmt.Sprintf("%q", importPath)
// Re-export interfaces. This is type-only and does not
// generate a require() call.
interfaces := i.interfaces()
if len(interfaces) > 0 {
fmt.Fprintf(w, "export { %s } from %s;\n",
strings.Join(interfaces, ", "),
quotedImport)
}
// Re-export function values into the value group, and install lazy loading.
funcs := i.functions()
for _, f := range funcs {
fmt.Fprintf(w, "export const %[1]s: typeof import(%[2]s).%[1]s = null as any;\n",
f, quotedImport)
}
ll.genLazyLoads(w, importPath, funcs...)
}