@znemz/cfn-include 2.1.20 → 2.1.22
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.
- package/bin/cli.js +2 -2
- package/index.js +25 -19
- package/lib/promise-utils.js +50 -0
- package/lib/promise.js +6 -6
- package/lib/scope.js +58 -0
- package/package.json +1 -3
package/bin/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const exec = require('child_process').execSync;
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const _ = require('lodash');
|
|
6
|
-
|
|
6
|
+
// path.parse is native in Node.js - no need for path-parse package
|
|
7
7
|
|
|
8
8
|
const include = require('../index');
|
|
9
9
|
const yaml = require('../lib/yaml');
|
|
@@ -118,7 +118,7 @@ if (opts.path) {
|
|
|
118
118
|
let location;
|
|
119
119
|
const protocol = opts.path.match(/^\w+:\/\//);
|
|
120
120
|
if (protocol) location = opts.path;
|
|
121
|
-
else if (
|
|
121
|
+
else if (path.parse(opts.path).root) location = `file://${opts.path}`;
|
|
122
122
|
else location = `file://${path.join(process.cwd(), opts.path)}`;
|
|
123
123
|
promise = include({
|
|
124
124
|
url: location,
|
package/index.js
CHANGED
|
@@ -2,12 +2,11 @@ const url = require('url');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const { glob } = require('glob');
|
|
5
|
-
const Promise = require('bluebird');
|
|
6
5
|
const sortObject = require('@znemz/sort-object');
|
|
7
6
|
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
|
|
8
7
|
const { addProxyToClient } = require('aws-sdk-v3-proxy');
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
// path.parse is native in Node.js - no need for path-parse package
|
|
11
10
|
const deepMerge = require('deepmerge');
|
|
12
11
|
const { isTaggableResource } = require('@znemz/cft-utils/src/resources/taggable');
|
|
13
12
|
|
|
@@ -26,6 +25,8 @@ const { lowerCamelCase, upperCamelCase } = require('./lib/utils');
|
|
|
26
25
|
const { isOurExplicitFunction } = require('./lib/schema');
|
|
27
26
|
const { getAwsPseudoParameters, buildResourceArn } = require('./lib/internals');
|
|
28
27
|
const { cachedReadFile } = require('./lib/cache');
|
|
28
|
+
const { createChildScope } = require('./lib/scope');
|
|
29
|
+
const { promiseProps } = require('./lib/promise-utils');
|
|
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
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
|
|
145
|
+
additions[idx] = key;
|
|
144
146
|
}
|
|
145
|
-
const
|
|
146
|
-
|
|
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);
|
|
@@ -582,7 +585,7 @@ async function recurse({ base, scope, cft, rootTemplate, caller, ...opts }) {
|
|
|
582
585
|
});
|
|
583
586
|
}
|
|
584
587
|
|
|
585
|
-
return
|
|
588
|
+
return promiseProps(
|
|
586
589
|
_.mapValues(cft, (template, key) => recurse({ base, scope, cft: template, key, rootTemplate, caller: 'recurse:isPlainObject:end', ...opts })),
|
|
587
590
|
);
|
|
588
591
|
}
|
|
@@ -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 (
|
|
598
|
-
|
|
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 =
|
|
604
|
+
object = scope[find];
|
|
601
605
|
}
|
|
602
|
-
}
|
|
606
|
+
}
|
|
603
607
|
}
|
|
604
|
-
if (
|
|
605
|
-
|
|
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
|
-
|
|
624
|
+
Object.keys(object).forEach(function (key) {
|
|
619
625
|
if (key === 'Fn::Map') return;
|
|
620
626
|
object[key] = findAndReplace(scope, object[key]);
|
|
621
627
|
});
|
|
@@ -737,7 +743,7 @@ async function fnInclude({ base, scope, cft, ...opts }) {
|
|
|
737
743
|
body = cachedReadFile(absolute).then(procTemplate);
|
|
738
744
|
absolute = `${location.protocol}://${absolute}`;
|
|
739
745
|
} else if (location.protocol === 's3') {
|
|
740
|
-
const basedir =
|
|
746
|
+
const basedir = path.parse(base.path).dir;
|
|
741
747
|
const bucket = location.relative ? base.host : location.host;
|
|
742
748
|
|
|
743
749
|
let key = location.relative ? url.resolve(`${basedir}/`, location.raw) : location.path;
|
|
@@ -753,7 +759,7 @@ async function fnInclude({ base, scope, cft, ...opts }) {
|
|
|
753
759
|
.then((res) => res.Body.toString())
|
|
754
760
|
.then(procTemplate);
|
|
755
761
|
} else if (location.protocol && location.protocol.match(/^https?$/)) {
|
|
756
|
-
const basepath = `${
|
|
762
|
+
const basepath = `${path.parse(base.path).dir}/`;
|
|
757
763
|
|
|
758
764
|
absolute = location.relative
|
|
759
765
|
? url.resolve(`${location.protocol}://${base.host}${basepath}`, location.raw)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native Promise utilities to replace bluebird.
|
|
3
|
+
* These are drop-in replacements for the bluebird methods we use.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Promise.props replacement - resolves an object of promises.
|
|
8
|
+
* @param {Object} obj - Object with promise values
|
|
9
|
+
* @returns {Promise<Object>} Object with resolved values
|
|
10
|
+
*/
|
|
11
|
+
async function promiseProps(obj) {
|
|
12
|
+
const keys = Object.keys(obj);
|
|
13
|
+
const values = await Promise.all(keys.map((key) => obj[key]));
|
|
14
|
+
const result = {};
|
|
15
|
+
keys.forEach((key, i) => {
|
|
16
|
+
result[key] = values[i];
|
|
17
|
+
});
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Promise.try replacement - wraps a function to catch sync errors.
|
|
23
|
+
* @param {Function} fn - Function to execute
|
|
24
|
+
* @returns {Promise} Promise that resolves to fn result or rejects on error
|
|
25
|
+
*/
|
|
26
|
+
function promiseTry(fn) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
try {
|
|
29
|
+
resolve(fn());
|
|
30
|
+
} catch (err) {
|
|
31
|
+
reject(err);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Promise.map replacement - maps over array with concurrency.
|
|
38
|
+
* @param {Array} arr - Array to map over
|
|
39
|
+
* @param {Function} fn - Async function to apply
|
|
40
|
+
* @returns {Promise<Array>} Array of resolved values
|
|
41
|
+
*/
|
|
42
|
+
function promiseMap(arr, fn) {
|
|
43
|
+
return Promise.all(arr.map(fn));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
promiseProps,
|
|
48
|
+
promiseTry,
|
|
49
|
+
promiseMap,
|
|
50
|
+
};
|
package/lib/promise.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
const
|
|
2
|
-
const _ = require('lodash');
|
|
1
|
+
const { promiseTry, promiseMap } = require('./promise-utils');
|
|
3
2
|
|
|
4
3
|
/*
|
|
5
4
|
Maps over objects or iterables just like lodash.
|
|
6
5
|
*/
|
|
7
6
|
const mapWhatever = (promises, cb) =>
|
|
8
|
-
|
|
7
|
+
promiseTry(() =>
|
|
9
8
|
Promise.resolve(promises).then((arrayOrObject) => {
|
|
10
|
-
if (
|
|
11
|
-
return
|
|
9
|
+
if (Array.isArray(arrayOrObject)) {
|
|
10
|
+
return promiseMap(arrayOrObject, cb);
|
|
12
11
|
}
|
|
13
12
|
const size = Object.values(arrayOrObject).length;
|
|
14
|
-
|
|
13
|
+
const entries = Object.entries(arrayOrObject);
|
|
14
|
+
return Promise.all(entries.map(([key, value]) => cb(value, key, size)));
|
|
15
15
|
}),
|
|
16
16
|
);
|
|
17
17
|
|
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.
|
|
3
|
+
"version": "2.1.22",
|
|
4
4
|
"description": "Preprocessor for CloudFormation templates with support for loops and flexible include statements",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aws",
|
|
@@ -48,14 +48,12 @@
|
|
|
48
48
|
"@znemz/cft-utils": "0.1.33",
|
|
49
49
|
"@znemz/sort-object": "^3.0.4",
|
|
50
50
|
"aws-sdk-v3-proxy": "2.2.0",
|
|
51
|
-
"bluebird": "^3.7.2",
|
|
52
51
|
"deepmerge": "^4.2.2",
|
|
53
52
|
"glob": "^13.0.0",
|
|
54
53
|
"jmespath": "^0.16.0",
|
|
55
54
|
"js-yaml": "^4.1.1",
|
|
56
55
|
"jsonminify": "^0.4.1",
|
|
57
56
|
"lodash": "^4.17.21",
|
|
58
|
-
"path-parse": "~1.0.7",
|
|
59
57
|
"proxy-agent": "6.5.0",
|
|
60
58
|
"yargs": "~18.0.0"
|
|
61
59
|
},
|