@znemz/cfn-include 2.1.20 → 2.1.21

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 (3) hide show
  1. package/index.js +20 -14
  2. package/lib/scope.js +58 -0
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -26,6 +26,7 @@ const { lowerCamelCase, upperCamelCase } = require('./lib/utils');
26
26
  const { isOurExplicitFunction } = require('./lib/schema');
27
27
  const { getAwsPseudoParameters, buildResourceArn } = require('./lib/internals');
28
28
  const { cachedReadFile } = require('./lib/cache');
29
+ const { createChildScope } = require('./lib/scope');
29
30
 
30
31
  /**
31
32
  * @param {object} options
@@ -109,7 +110,8 @@ async function recurse({ base, scope, cft, rootTemplate, caller, ...opts }) {
109
110
  if (opts.doLog) {
110
111
  console.log({ base, scope, cft, rootTemplate, caller, ...opts });
111
112
  }
112
- scope = _.clone(scope);
113
+ // Use Object.create() for O(1) child scope creation instead of O(n) clone
114
+ scope = createChildScope(scope);
113
115
  if (Array.isArray(cft)) {
114
116
  return Promise.all(cft.map((o) => recurse({ base, scope, cft: o, rootTemplate, caller: 'recurse:isArray', ...opts })));
115
117
  }
@@ -137,13 +139,14 @@ async function recurse({ base, scope, cft, rootTemplate, caller, ...opts }) {
137
139
  placeholder = '_';
138
140
  }
139
141
  return PromiseExt.mapX(recurse({ base, scope, cft: list, rootTemplate, caller: 'Fn::Map', ...opts }), (replace, key) => {
140
- scope = _.clone(scope);
141
- scope[placeholder] = replace;
142
+ // Use Object.create() for O(1) child scope creation instead of O(n) clone
143
+ const additions = { [placeholder]: replace };
142
144
  if (hasindex) {
143
- scope[idx] = key;
145
+ additions[idx] = key;
144
146
  }
145
- const replaced = findAndReplace(scope, _.cloneDeep(body));
146
- return recurse({ base, scope, cft: replaced, rootTemplate, caller: 'Fn::Map', ...opts });
147
+ const childScope = createChildScope(scope, additions);
148
+ const replaced = findAndReplace(childScope, _.cloneDeep(body));
149
+ return recurse({ base, scope: childScope, cft: replaced, rootTemplate, caller: 'Fn::Map', ...opts });
147
150
  }).then((_cft) => {
148
151
  if (hassize) {
149
152
  _cft = findAndReplace({ [sz]: _cft.length }, _cft);
@@ -594,20 +597,23 @@ async function recurse({ base, scope, cft, rootTemplate, caller, ...opts }) {
594
597
  }
595
598
 
596
599
  function findAndReplace(scope, object) {
597
- if (_.isString(object)) {
598
- _.forEach(scope, function (replace, find) {
600
+ if (typeof object === 'string') {
601
+ // Use for...in to walk prototype chain (Object.create() based scopes)
602
+ for (const find in scope) {
599
603
  if (object === find) {
600
- object = replace;
604
+ object = scope[find];
601
605
  }
602
- });
606
+ }
603
607
  }
604
- if (_.isString(object)) {
605
- _.forEach(scope, function (replace, find) {
608
+ if (typeof object === 'string') {
609
+ // Use for...in to walk prototype chain (Object.create() based scopes)
610
+ for (const find in scope) {
611
+ const replace = scope[find];
606
612
  const regex = new RegExp(`\\\${${find}}`, 'g');
607
613
  if (find !== '_' && object.match(regex)) {
608
614
  object = object.replace(regex, replace);
609
615
  }
610
- });
616
+ }
611
617
  }
612
618
  if (Array.isArray(object)) {
613
619
  object = object.map(_.bind(findAndReplace, this, scope));
@@ -615,7 +621,7 @@ function findAndReplace(scope, object) {
615
621
  object = _.mapKeys(object, function (value, key) {
616
622
  return findAndReplace(scope, key);
617
623
  });
618
- _.keys(object).forEach(function (key) {
624
+ Object.keys(object).forEach(function (key) {
619
625
  if (key === 'Fn::Map') return;
620
626
  object[key] = findAndReplace(scope, object[key]);
621
627
  });
package/lib/scope.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Scope helper functions for lazy prototype-chain based scope management.
3
+ *
4
+ * Instead of _.clone(scope) which copies O(n) properties each time,
5
+ * we use Object.create(scope) which creates a child scope in O(1) time
6
+ * that inherits from the parent via the prototype chain.
7
+ */
8
+
9
+ /**
10
+ * Create a child scope that inherits from the parent.
11
+ * Uses Object.create() for O(1) creation instead of cloning.
12
+ *
13
+ * @param {Object} parent - The parent scope to inherit from
14
+ * @param {Object} [additions={}] - Properties to add to the child scope
15
+ * @returns {Object} A new child scope with prototype chain to parent
16
+ */
17
+ function createChildScope(parent, additions = {}) {
18
+ const child = Object.create(parent);
19
+ Object.assign(child, additions);
20
+ return child;
21
+ }
22
+
23
+ /**
24
+ * Convert a prototype-chain scope to a plain object.
25
+ * Uses for...in to walk the entire prototype chain.
26
+ *
27
+ * Useful when we need to pass scope to functions that don't
28
+ * walk the prototype chain (e.g., Object.keys, _.forEach).
29
+ *
30
+ * @param {Object} scope - The scope to flatten
31
+ * @returns {Object} A plain object with all inherited properties
32
+ */
33
+ function scopeToObject(scope) {
34
+ const result = {};
35
+ for (const key in scope) {
36
+ result[key] = scope[key];
37
+ }
38
+ return result;
39
+ }
40
+
41
+ /**
42
+ * Iterate over all properties in a scope, including inherited ones.
43
+ * This is a replacement for _.forEach that walks the prototype chain.
44
+ *
45
+ * @param {Object} scope - The scope to iterate over
46
+ * @param {Function} callback - Function to call with (value, key)
47
+ */
48
+ function forEachInScope(scope, callback) {
49
+ for (const key in scope) {
50
+ callback(scope[key], key);
51
+ }
52
+ }
53
+
54
+ module.exports = {
55
+ createChildScope,
56
+ scopeToObject,
57
+ forEachInScope,
58
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@znemz/cfn-include",
3
- "version": "2.1.20",
3
+ "version": "2.1.21",
4
4
  "description": "Preprocessor for CloudFormation templates with support for loops and flexible include statements",
5
5
  "keywords": [
6
6
  "aws",