@znemz/cfn-include 2.1.19 → 2.1.20

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 +18 -18
  2. package/lib/cache.js +34 -0
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  const url = require('url');
2
2
  const path = require('path');
3
- const { readFile } = require('fs/promises');
4
3
  const _ = require('lodash');
5
- const { globSync } = require('glob');
4
+ const { glob } = require('glob');
6
5
  const Promise = require('bluebird');
7
6
  const sortObject = require('@znemz/sort-object');
8
7
  const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
@@ -26,6 +25,7 @@ const replaceEnv = require('./lib/replaceEnv');
26
25
  const { lowerCamelCase, upperCamelCase } = require('./lib/utils');
27
26
  const { isOurExplicitFunction } = require('./lib/schema');
28
27
  const { getAwsPseudoParameters, buildResourceArn } = require('./lib/internals');
28
+ const { cachedReadFile } = require('./lib/cache');
29
29
 
30
30
  /**
31
31
  * @param {object} options
@@ -60,7 +60,7 @@ module.exports = async function (options) {
60
60
  const base = parseLocation(options.url);
61
61
  const scope = options.scope || {};
62
62
  if (base.relative) throw new Error('url cannot be relative');
63
- template = _.isUndefined(template)
63
+ template = !template
64
64
  ? fnInclude({ base, scope, cft: options.url, ...options })
65
65
  : template;
66
66
  // Resolve template if it's a promise to extract the root template for reference lookups
@@ -110,7 +110,7 @@ async function recurse({ base, scope, cft, rootTemplate, caller, ...opts }) {
110
110
  console.log({ base, scope, cft, rootTemplate, caller, ...opts });
111
111
  }
112
112
  scope = _.clone(scope);
113
- if (_.isArray(cft)) {
113
+ if (Array.isArray(cft)) {
114
114
  return Promise.all(cft.map((o) => recurse({ base, scope, cft: o, rootTemplate, caller: 'recurse:isArray', ...opts })));
115
115
  }
116
116
  if (_.isPlainObject(cft)) {
@@ -175,24 +175,24 @@ async function recurse({ base, scope, cft, rootTemplate, caller, ...opts }) {
175
175
  }
176
176
  if (cft['Fn::Flatten']) {
177
177
  return recurse({ base, scope, cft: cft['Fn::Flatten'], rootTemplate, caller: 'Fn::Flatten', ...opts }).then(function (json) {
178
- return _.flatten(json);
178
+ return json.flat();
179
179
  });
180
180
  }
181
181
  if (cft['Fn::FlattenDeep']) {
182
182
  return recurse({ base, scope, cft: cft['Fn::FlattenDeep'], rootTemplate, caller: 'Fn::FlattenDeep', ...opts }).then(
183
183
  function (json) {
184
- return _.flattenDeep(json);
184
+ return json.flat(Infinity);
185
185
  },
186
186
  );
187
187
  }
188
188
  if (cft['Fn::Uniq']) {
189
189
  return recurse({ base, scope, cft: cft['Fn::Uniq'], rootTemplate, caller: 'Fn::Uniq', ...opts }).then(function (json) {
190
- return _.uniq(json);
190
+ return [...new Set(json)];
191
191
  });
192
192
  }
193
193
  if (cft['Fn::Compact']) {
194
194
  return recurse({ base, scope, cft: cft['Fn::Compact'], rootTemplate, caller: 'Fn::Compact', ...opts }).then(function (json) {
195
- return _.compact(json);
195
+ return json.filter(Boolean);
196
196
  });
197
197
  }
198
198
  if (cft['Fn::Concat']) {
@@ -269,7 +269,7 @@ async function recurse({ base, scope, cft, rootTemplate, caller, ...opts }) {
269
269
  }
270
270
  if (cft['Fn::Filenames']) {
271
271
  return recurse({ base, scope, cft: cft['Fn::Filenames'], rootTemplate, caller: 'Fn::Filenames', ...opts }).then(
272
- function (json) {
272
+ async function (json) {
273
273
  json = _.isPlainObject(json) ? { ...json } : { location: json };
274
274
  if (json.doLog) {
275
275
 
@@ -284,7 +284,7 @@ async function recurse({ base, scope, cft, rootTemplate, caller, ...opts }) {
284
284
  const absolute = location.relative
285
285
  ? path.join(path.dirname(base.path), location.host, location.path || '')
286
286
  : [location.host, location.path].join('');
287
- const globs = globSync(absolute).sort();
287
+ const globs = (await glob(absolute)).sort();
288
288
  if (json.omitExtension) {
289
289
  return globs.map((f) => path.basename(f, path.extname(f)));
290
290
  }
@@ -587,7 +587,7 @@ async function recurse({ base, scope, cft, rootTemplate, caller, ...opts }) {
587
587
  );
588
588
  }
589
589
 
590
- if (_.isUndefined(cft)) {
590
+ if (cft === undefined) {
591
591
  return null;
592
592
  }
593
593
  return replaceEnv(cft, opts.inject, opts.doEnv);
@@ -609,7 +609,7 @@ function findAndReplace(scope, object) {
609
609
  }
610
610
  });
611
611
  }
612
- if (_.isArray(object)) {
612
+ if (Array.isArray(object)) {
613
613
  object = object.map(_.bind(findAndReplace, this, scope));
614
614
  } else if (_.isPlainObject(object)) {
615
615
  object = _.mapKeys(object, function (value, key) {
@@ -632,7 +632,7 @@ function interpolate(lines, context) {
632
632
  const match = _line.match(/^{{(\w+)}}$/);
633
633
  const value = match ? context[match[1]] : undefined;
634
634
  if (!match) return _line;
635
- if (_.isUndefined(value)) {
635
+ if (value === undefined) {
636
636
  return '';
637
637
  }
638
638
  return value;
@@ -661,7 +661,7 @@ function fnIncludeOptsFromArray(cft, opts) {
661
661
  function fnIncludeOpts(cft, opts) {
662
662
  if (_.isPlainObject(cft)) {
663
663
  cft = _.merge(cft, _.cloneDeep(opts));
664
- } else if (_.isArray(cft)) {
664
+ } else if (Array.isArray(cft)) {
665
665
  cft = fnIncludeOptsFromArray(cft, opts);
666
666
  } else {
667
667
  // should be string{
@@ -730,11 +730,11 @@ async function fnInclude({ base, scope, cft, ...opts }) {
730
730
 
731
731
  handleInjectSetup();
732
732
  if (isGlob(cft, absolute)) {
733
- const paths = globSync(absolute).sort();
733
+ const paths = (await glob(absolute)).sort();
734
734
  const template = yaml.load(paths.map((_p) => `- Fn::Include: file://${_p}`).join('\n'));
735
735
  return recurse({ base, scope, cft: template, rootTemplate: template, ...opts });
736
736
  }
737
- body = readFile(absolute).then(String).then(procTemplate);
737
+ body = cachedReadFile(absolute).then(procTemplate);
738
738
  absolute = `${location.protocol}://${absolute}`;
739
739
  } else if (location.protocol === 's3') {
740
740
  const basedir = pathParse(base.path).dir;
@@ -829,7 +829,7 @@ async function handleIncludeBody({ scope, args, body, absolute }) {
829
829
  return temp;
830
830
  }
831
831
  // once fully recursed we can query the resultant template
832
- const query = _.isString(args.query)
832
+ const query = typeof args.query === 'string'
833
833
  ? replaceEnv(args.query, args.inject, args.doEnv)
834
834
  : await recurse({
835
835
  base: parseLocation(absolute),
@@ -858,7 +858,7 @@ async function handleIncludeBody({ scope, args, body, absolute }) {
858
858
  lines = interpolate(lines, args.context);
859
859
  }
860
860
  return {
861
- 'Fn::Join': ['', _.flatten(lines)],
861
+ 'Fn::Join': ['', lines.flat()],
862
862
  };
863
863
  });
864
864
  }
package/lib/cache.js ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * File content cache to avoid redundant disk I/O.
3
+ */
4
+
5
+ const { readFile } = require('fs/promises');
6
+
7
+ // File content cache to avoid re-reading the same files
8
+ const fileCache = new Map();
9
+
10
+ /**
11
+ * Read a file with caching to avoid redundant disk I/O
12
+ * @param {string} absolutePath - Absolute path to the file
13
+ * @returns {Promise<string>} File content as a string
14
+ */
15
+ async function cachedReadFile(absolutePath) {
16
+ if (fileCache.has(absolutePath)) {
17
+ return fileCache.get(absolutePath);
18
+ }
19
+ const content = await readFile(absolutePath, 'utf8');
20
+ fileCache.set(absolutePath, content);
21
+ return content;
22
+ }
23
+
24
+ /**
25
+ * Clear the file cache (useful for testing)
26
+ */
27
+ function clearFileCache() {
28
+ fileCache.clear();
29
+ }
30
+
31
+ module.exports = {
32
+ cachedReadFile,
33
+ clearFileCache,
34
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@znemz/cfn-include",
3
- "version": "2.1.19",
3
+ "version": "2.1.20",
4
4
  "description": "Preprocessor for CloudFormation templates with support for loops and flexible include statements",
5
5
  "keywords": [
6
6
  "aws",