github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/nodejs/gen_lazyloads.go (about)

     1  // Copyright 2016-2022, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Large providers like azure-native generate code that has a high
    16  // startup overhead in Node JS due to loading thousands of modules at
    17  // once. The code in this file is dedicated to support on-demand
    18  // (lazy) loading of modules at Node level to speed up program
    19  // startup.
    20  
    21  package nodejs
    22  
    23  import (
    24  	"encoding/json"
    25  	"fmt"
    26  	"io"
    27  	"sort"
    28  	"strings"
    29  
    30  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
    31  )
    32  
    33  type lazyLoadGen struct{}
    34  
    35  func newLazyLoadGen() *lazyLoadGen {
    36  	return &lazyLoadGen{}
    37  }
    38  
    39  // Generates TypeScript code to re-export a generated module. For
    40  // resources and functions this is optimized to use lazy loading.
    41  // Falls back to eager re-export for everything else.
    42  func (ll *lazyLoadGen) genReexport(w io.Writer, exp fileInfo, importPath string) {
    43  	if exp.fileType == functionFileType {
    44  		// optimize lazy-loading function modules
    45  		ll.genFunctionReexport(w, exp.functionFileInfo, importPath)
    46  	} else if exp.fileType == resourceFileType {
    47  		// optimize lazy-loading resource modules
    48  		ll.genResourceReexport(w, exp.resourceFileInfo, importPath)
    49  	} else {
    50  		// non-optimized but foolproof eager reexport
    51  		fmt.Fprintf(w, "export * from %q;\n", importPath)
    52  	}
    53  }
    54  
    55  // Used as a follow up to multiple genReexport calls to generate a
    56  // lazy-load polyfill to all the properties that need that.
    57  func (*lazyLoadGen) genLazyLoads(w io.Writer, importPath string, properties ...string) {
    58  	sort.Strings(properties)
    59  	j, err := json.Marshal(properties)
    60  	contract.AssertNoError(err)
    61  	fmt.Fprintf(w, "utilities.lazyLoad(exports, %s, () => require(%q));\n",
    62  		string(j), importPath)
    63  }
    64  
    65  // Generates TypeScript code that lazily imports and re-exports a
    66  // module defining a resource, while also importing the resoure class
    67  // in-scope. Needs to know which names the module defines
    68  // (resourceFileInfo).
    69  func (ll *lazyLoadGen) genResourceReexport(w io.Writer, i resourceFileInfo, importPath string) {
    70  	defer fmt.Fprintf(w, "\n")
    71  
    72  	quotedImport := fmt.Sprintf("%q", importPath)
    73  
    74  	// not sure how to lazy-load in presence of re-exported
    75  	// namespaces; bail and use an eager load in this case; also
    76  	// eager-import the class.
    77  	if i.methodsNamespaceName != "" {
    78  		fmt.Fprintf(w, "export * from %s;\n", quotedImport)
    79  		fmt.Fprintf(w, "import { %s } from %s;\n", i.resourceClassName, quotedImport)
    80  		return
    81  	}
    82  
    83  	// Re-export interfaces. This is type-only and does not
    84  	// generate a require() call.
    85  	fmt.Fprintf(w, "export { %s } from %s;\n",
    86  		strings.Join(i.interfaces(), ", "),
    87  		quotedImport)
    88  
    89  	// Re-export class type into the type group, see
    90  	// https://www.typescriptlang.org/docs/handbook/declaration-merging.html
    91  	fmt.Fprintf(w, "export type %[1]s = import(%[2]s).%[1]s;\n",
    92  		i.resourceClassName,
    93  		quotedImport)
    94  
    95  	// Mock re-export class value into the value group - for compilation.
    96  	fmt.Fprintf(w, "export const %[1]s: typeof import(%[2]s).%[1]s = null as any;\n",
    97  		i.resourceClassName,
    98  		quotedImport)
    99  
   100  	ll.genLazyLoads(w, importPath, i.resourceClassName)
   101  }
   102  
   103  // Generates TypeScript code that lazily imports and re-exports a
   104  // module defining a function. Needs to which names the module defines
   105  // (functionFileInfo).
   106  func (ll *lazyLoadGen) genFunctionReexport(w io.Writer, i functionFileInfo, importPath string) {
   107  	defer fmt.Fprintf(w, "\n")
   108  
   109  	quotedImport := fmt.Sprintf("%q", importPath)
   110  
   111  	// Re-export interfaces. This is type-only and does not
   112  	// generate a require() call.
   113  	interfaces := i.interfaces()
   114  	if len(interfaces) > 0 {
   115  		fmt.Fprintf(w, "export { %s } from %s;\n",
   116  			strings.Join(interfaces, ", "),
   117  			quotedImport)
   118  	}
   119  
   120  	// Re-export function values into the value group, and install lazy loading.
   121  	funcs := i.functions()
   122  	for _, f := range funcs {
   123  		fmt.Fprintf(w, "export const %[1]s: typeof import(%[2]s).%[1]s = null as any;\n",
   124  			f, quotedImport)
   125  	}
   126  	ll.genLazyLoads(w, importPath, funcs...)
   127  }