parse-hcl 0.1.0
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/LICENSE +201 -0
- package/README.md +749 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +91 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +74 -0
- package/dist/parsers/genericParser.d.ts +167 -0
- package/dist/parsers/genericParser.js +268 -0
- package/dist/parsers/localsParser.d.ts +30 -0
- package/dist/parsers/localsParser.js +43 -0
- package/dist/parsers/outputParser.d.ts +25 -0
- package/dist/parsers/outputParser.js +44 -0
- package/dist/parsers/variableParser.d.ts +62 -0
- package/dist/parsers/variableParser.js +249 -0
- package/dist/services/artifactParsers.d.ts +12 -0
- package/dist/services/artifactParsers.js +157 -0
- package/dist/services/terraformJsonParser.d.ts +16 -0
- package/dist/services/terraformJsonParser.js +212 -0
- package/dist/services/terraformParser.d.ts +91 -0
- package/dist/services/terraformParser.js +191 -0
- package/dist/types/artifacts.d.ts +210 -0
- package/dist/types/artifacts.js +5 -0
- package/dist/types/blocks.d.ts +419 -0
- package/dist/types/blocks.js +28 -0
- package/dist/utils/common/errors.d.ts +46 -0
- package/dist/utils/common/errors.js +54 -0
- package/dist/utils/common/fs.d.ts +5 -0
- package/dist/utils/common/fs.js +48 -0
- package/dist/utils/common/logger.d.ts +5 -0
- package/dist/utils/common/logger.js +17 -0
- package/dist/utils/common/valueHelpers.d.ts +4 -0
- package/dist/utils/common/valueHelpers.js +23 -0
- package/dist/utils/graph/graphBuilder.d.ts +33 -0
- package/dist/utils/graph/graphBuilder.js +373 -0
- package/dist/utils/lexer/blockScanner.d.ts +36 -0
- package/dist/utils/lexer/blockScanner.js +143 -0
- package/dist/utils/lexer/hclLexer.d.ts +119 -0
- package/dist/utils/lexer/hclLexer.js +525 -0
- package/dist/utils/parser/bodyParser.d.ts +26 -0
- package/dist/utils/parser/bodyParser.js +81 -0
- package/dist/utils/parser/valueClassifier.d.ts +21 -0
- package/dist/utils/parser/valueClassifier.js +434 -0
- package/dist/utils/serialization/serializer.d.ts +9 -0
- package/dist/utils/serialization/serializer.js +63 -0
- package/dist/utils/serialization/yaml.d.ts +1 -0
- package/dist/utils/serialization/yaml.js +81 -0
- package/package.json +66 -0
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const terraformParser_1 = require("./services/terraformParser");
|
|
9
|
+
const artifactParsers_1 = require("./services/artifactParsers");
|
|
10
|
+
const serializer_1 = require("./utils/serialization/serializer");
|
|
11
|
+
function parseArgs(argv) {
|
|
12
|
+
const opts = { format: 'json', graph: false, prune: true };
|
|
13
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
14
|
+
const arg = argv[i];
|
|
15
|
+
if (arg === '--file' && argv[i + 1]) {
|
|
16
|
+
opts.file = argv[++i];
|
|
17
|
+
}
|
|
18
|
+
else if (arg === '--dir' && argv[i + 1]) {
|
|
19
|
+
opts.dir = argv[++i];
|
|
20
|
+
}
|
|
21
|
+
else if (arg === '--format' && argv[i + 1]) {
|
|
22
|
+
const fmt = argv[++i];
|
|
23
|
+
if (fmt === 'json' || fmt === 'yaml') {
|
|
24
|
+
opts.format = fmt;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else if (arg === '--graph') {
|
|
28
|
+
opts.graph = true;
|
|
29
|
+
}
|
|
30
|
+
else if (arg === '--no-prune') {
|
|
31
|
+
opts.prune = false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return opts;
|
|
35
|
+
}
|
|
36
|
+
function main() {
|
|
37
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
38
|
+
const parser = new terraformParser_1.TerraformParser();
|
|
39
|
+
if (!opts.file && !opts.dir) {
|
|
40
|
+
console.error('Usage: parse-hcl --file <path> | --dir <path> [--format json|yaml] [--graph] [--no-prune]');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
if (opts.file) {
|
|
44
|
+
const filePath = path_1.default.resolve(opts.file);
|
|
45
|
+
const ext = path_1.default.extname(filePath);
|
|
46
|
+
if (ext.includes('tfvars')) {
|
|
47
|
+
emit(tfvarsParse(filePath), opts);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (ext === '.tfstate') {
|
|
51
|
+
emit(new artifactParsers_1.TfStateParser().parseFile(filePath), opts);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (ext === '.json' && filePath.endsWith('plan.json')) {
|
|
55
|
+
emit(new artifactParsers_1.TfPlanParser().parseFile(filePath), opts);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const doc = parser.parseFile(filePath);
|
|
59
|
+
emit(doc, opts);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (opts.dir) {
|
|
63
|
+
const dirPath = path_1.default.resolve(opts.dir);
|
|
64
|
+
const result = parser.parseDirectory(dirPath);
|
|
65
|
+
const combined = result.combined ?? parser.combine(result.files.map((f) => f.document));
|
|
66
|
+
emit(opts.graph ? (0, serializer_1.toExport)(combined, { pruneEmpty: opts.prune }) : result, opts);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function tfvarsParse(filePath) {
|
|
70
|
+
return new artifactParsers_1.TfVarsParser().parseFile(filePath);
|
|
71
|
+
}
|
|
72
|
+
function emit(data, opts) {
|
|
73
|
+
if (opts.graph && !isTerraformDoc(data)) {
|
|
74
|
+
console.warn('Graph export requested but input is not a Terraform document; emitting raw output.');
|
|
75
|
+
}
|
|
76
|
+
if (opts.format === 'yaml') {
|
|
77
|
+
console.info((0, serializer_1.toYamlDocument)(data, { pruneEmpty: opts.prune }));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (opts.graph && isTerraformDoc(data)) {
|
|
81
|
+
console.info((0, serializer_1.toJsonExport)(data, { pruneEmpty: opts.prune }));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.info((0, serializer_1.toJson)(data, { pruneEmpty: opts.prune }));
|
|
85
|
+
}
|
|
86
|
+
function isTerraformDoc(data) {
|
|
87
|
+
return Boolean(data && typeof data === 'object' && 'resource' in data);
|
|
88
|
+
}
|
|
89
|
+
if (require.main === module) {
|
|
90
|
+
main();
|
|
91
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* parse-hcl - Lightweight Terraform HCL Parser
|
|
3
|
+
*
|
|
4
|
+
* A TypeScript library for parsing Terraform configuration files (.tf, .tf.json)
|
|
5
|
+
* and related artifacts (tfvars, tfstate, plan.json).
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*
|
|
9
|
+
* @example Basic Usage
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { TerraformParser, toJson, buildDependencyGraph } from 'parse-hcl';
|
|
12
|
+
*
|
|
13
|
+
* const parser = new TerraformParser();
|
|
14
|
+
*
|
|
15
|
+
* // Parse a single file
|
|
16
|
+
* const doc = parser.parseFile('main.tf');
|
|
17
|
+
*
|
|
18
|
+
* // Parse a directory
|
|
19
|
+
* const result = parser.parseDirectory('./terraform');
|
|
20
|
+
*
|
|
21
|
+
* // Build dependency graph
|
|
22
|
+
* const graph = buildDependencyGraph(doc);
|
|
23
|
+
*
|
|
24
|
+
* // Serialize to JSON
|
|
25
|
+
* const json = toJson(doc);
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @example Working with Artifacts
|
|
29
|
+
* ```typescript
|
|
30
|
+
* import { TfVarsParser, TfStateParser, TfPlanParser } from 'parse-hcl';
|
|
31
|
+
*
|
|
32
|
+
* // Parse tfvars
|
|
33
|
+
* const tfvars = new TfVarsParser().parseFile('terraform.tfvars');
|
|
34
|
+
*
|
|
35
|
+
* // Parse state
|
|
36
|
+
* const state = new TfStateParser().parseFile('terraform.tfstate');
|
|
37
|
+
*
|
|
38
|
+
* // Parse plan
|
|
39
|
+
* const plan = new TfPlanParser().parseFile('plan.json');
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export * from './types/blocks';
|
|
43
|
+
export * from './types/artifacts';
|
|
44
|
+
export * from './services/terraformParser';
|
|
45
|
+
export * from './services/artifactParsers';
|
|
46
|
+
export * from './services/terraformJsonParser';
|
|
47
|
+
export * from './utils/serialization/serializer';
|
|
48
|
+
export * from './utils/graph/graphBuilder';
|
|
49
|
+
export * from './utils/common/errors';
|
|
50
|
+
export { classifyValue } from './utils/parser/valueClassifier';
|
|
51
|
+
export { parseTypeConstraint } from './parsers/variableParser';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* parse-hcl - Lightweight Terraform HCL Parser
|
|
4
|
+
*
|
|
5
|
+
* A TypeScript library for parsing Terraform configuration files (.tf, .tf.json)
|
|
6
|
+
* and related artifacts (tfvars, tfstate, plan.json).
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*
|
|
10
|
+
* @example Basic Usage
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { TerraformParser, toJson, buildDependencyGraph } from 'parse-hcl';
|
|
13
|
+
*
|
|
14
|
+
* const parser = new TerraformParser();
|
|
15
|
+
*
|
|
16
|
+
* // Parse a single file
|
|
17
|
+
* const doc = parser.parseFile('main.tf');
|
|
18
|
+
*
|
|
19
|
+
* // Parse a directory
|
|
20
|
+
* const result = parser.parseDirectory('./terraform');
|
|
21
|
+
*
|
|
22
|
+
* // Build dependency graph
|
|
23
|
+
* const graph = buildDependencyGraph(doc);
|
|
24
|
+
*
|
|
25
|
+
* // Serialize to JSON
|
|
26
|
+
* const json = toJson(doc);
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @example Working with Artifacts
|
|
30
|
+
* ```typescript
|
|
31
|
+
* import { TfVarsParser, TfStateParser, TfPlanParser } from 'parse-hcl';
|
|
32
|
+
*
|
|
33
|
+
* // Parse tfvars
|
|
34
|
+
* const tfvars = new TfVarsParser().parseFile('terraform.tfvars');
|
|
35
|
+
*
|
|
36
|
+
* // Parse state
|
|
37
|
+
* const state = new TfStateParser().parseFile('terraform.tfstate');
|
|
38
|
+
*
|
|
39
|
+
* // Parse plan
|
|
40
|
+
* const plan = new TfPlanParser().parseFile('plan.json');
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
44
|
+
if (k2 === undefined) k2 = k;
|
|
45
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
46
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
47
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
48
|
+
}
|
|
49
|
+
Object.defineProperty(o, k2, desc);
|
|
50
|
+
}) : (function(o, m, k, k2) {
|
|
51
|
+
if (k2 === undefined) k2 = k;
|
|
52
|
+
o[k2] = m[k];
|
|
53
|
+
}));
|
|
54
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
55
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
56
|
+
};
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
exports.parseTypeConstraint = exports.classifyValue = void 0;
|
|
59
|
+
// Type definitions
|
|
60
|
+
__exportStar(require("./types/blocks"), exports);
|
|
61
|
+
__exportStar(require("./types/artifacts"), exports);
|
|
62
|
+
// Main parsers
|
|
63
|
+
__exportStar(require("./services/terraformParser"), exports);
|
|
64
|
+
__exportStar(require("./services/artifactParsers"), exports);
|
|
65
|
+
__exportStar(require("./services/terraformJsonParser"), exports);
|
|
66
|
+
// Utilities
|
|
67
|
+
__exportStar(require("./utils/serialization/serializer"), exports);
|
|
68
|
+
__exportStar(require("./utils/graph/graphBuilder"), exports);
|
|
69
|
+
__exportStar(require("./utils/common/errors"), exports);
|
|
70
|
+
// Re-export commonly used utilities
|
|
71
|
+
var valueClassifier_1 = require("./utils/parser/valueClassifier");
|
|
72
|
+
Object.defineProperty(exports, "classifyValue", { enumerable: true, get: function () { return valueClassifier_1.classifyValue; } });
|
|
73
|
+
var variableParser_1 = require("./parsers/variableParser");
|
|
74
|
+
Object.defineProperty(exports, "parseTypeConstraint", { enumerable: true, get: function () { return variableParser_1.parseTypeConstraint; } });
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic parsers for Terraform blocks.
|
|
3
|
+
* Handles resource, data, provider, module, terraform settings, and unknown block types.
|
|
4
|
+
*/
|
|
5
|
+
import { DataBlock, GenericBlock, ModuleBlock, ProviderBlock, ResourceBlock, TerraformSettingsBlock } from '../types/blocks';
|
|
6
|
+
import { HclBlock } from '../types/blocks';
|
|
7
|
+
/**
|
|
8
|
+
* Parser for terraform settings blocks.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```hcl
|
|
12
|
+
* terraform {
|
|
13
|
+
* required_version = ">= 1.0.0"
|
|
14
|
+
* required_providers {
|
|
15
|
+
* aws = {
|
|
16
|
+
* source = "hashicorp/aws"
|
|
17
|
+
* version = "~> 4.0"
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class TerraformSettingsParser {
|
|
24
|
+
/**
|
|
25
|
+
* Parses a terraform settings block.
|
|
26
|
+
* @param block - The raw HCL block to parse
|
|
27
|
+
* @returns Parsed TerraformSettingsBlock
|
|
28
|
+
*/
|
|
29
|
+
parse(block: HclBlock): TerraformSettingsBlock;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parser for provider configuration blocks.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```hcl
|
|
36
|
+
* provider "aws" {
|
|
37
|
+
* alias = "west"
|
|
38
|
+
* region = "us-west-2"
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare class ProviderParser {
|
|
43
|
+
/**
|
|
44
|
+
* Parses a provider configuration block.
|
|
45
|
+
* @param block - The raw HCL block to parse
|
|
46
|
+
* @returns Parsed ProviderBlock with name, alias, and properties
|
|
47
|
+
*/
|
|
48
|
+
parse(block: HclBlock): ProviderBlock;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Parser for module call blocks.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```hcl
|
|
55
|
+
* module "vpc" {
|
|
56
|
+
* source = "terraform-aws-modules/vpc/aws"
|
|
57
|
+
* version = "3.0.0"
|
|
58
|
+
*
|
|
59
|
+
* name = "my-vpc"
|
|
60
|
+
* cidr = "10.0.0.0/16"
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare class ModuleParser {
|
|
65
|
+
/**
|
|
66
|
+
* Parses a module call block.
|
|
67
|
+
* @param block - The raw HCL block to parse
|
|
68
|
+
* @returns Parsed ModuleBlock with name and all properties
|
|
69
|
+
*/
|
|
70
|
+
parse(block: HclBlock): ModuleBlock;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parser for resource definition blocks.
|
|
74
|
+
* Separates meta-arguments (count, for_each, depends_on, etc.) from resource properties.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```hcl
|
|
78
|
+
* resource "aws_instance" "web" {
|
|
79
|
+
* count = 3
|
|
80
|
+
* ami = var.ami_id
|
|
81
|
+
* instance_type = "t2.micro"
|
|
82
|
+
*
|
|
83
|
+
* tags = {
|
|
84
|
+
* Name = "web-${count.index}"
|
|
85
|
+
* }
|
|
86
|
+
*
|
|
87
|
+
* dynamic "ebs_block_device" {
|
|
88
|
+
* for_each = var.ebs_volumes
|
|
89
|
+
* content {
|
|
90
|
+
* device_name = ebs_block_device.value.device_name
|
|
91
|
+
* volume_size = ebs_block_device.value.size
|
|
92
|
+
* }
|
|
93
|
+
* }
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare class ResourceParser {
|
|
98
|
+
/**
|
|
99
|
+
* Parses a resource block, separating properties, meta-arguments, and dynamic blocks.
|
|
100
|
+
* @param block - The raw HCL block to parse
|
|
101
|
+
* @returns Parsed ResourceBlock with separated concerns
|
|
102
|
+
*/
|
|
103
|
+
parse(block: HclBlock): ResourceBlock;
|
|
104
|
+
/**
|
|
105
|
+
* Extracts dynamic blocks from nested blocks.
|
|
106
|
+
* @param blocks - Nested blocks from the resource body
|
|
107
|
+
* @returns Array of DynamicBlock objects
|
|
108
|
+
*/
|
|
109
|
+
private extractDynamicBlocks;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Parser for data source blocks.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```hcl
|
|
116
|
+
* data "aws_ami" "ubuntu" {
|
|
117
|
+
* most_recent = true
|
|
118
|
+
*
|
|
119
|
+
* filter {
|
|
120
|
+
* name = "name"
|
|
121
|
+
* values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
|
|
122
|
+
* }
|
|
123
|
+
*
|
|
124
|
+
* owners = ["099720109477"]
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare class DataParser {
|
|
129
|
+
/**
|
|
130
|
+
* Parses a data source block.
|
|
131
|
+
* @param block - The raw HCL block to parse
|
|
132
|
+
* @returns Parsed DataBlock with type, name, properties, and nested blocks
|
|
133
|
+
*/
|
|
134
|
+
parse(block: HclBlock): DataBlock;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Parser for generic/unknown block types.
|
|
138
|
+
* Used for moved, import, check, terraform_data, and unrecognized blocks.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```hcl
|
|
142
|
+
* moved {
|
|
143
|
+
* from = aws_instance.old
|
|
144
|
+
* to = aws_instance.new
|
|
145
|
+
* }
|
|
146
|
+
*
|
|
147
|
+
* import {
|
|
148
|
+
* to = aws_instance.example
|
|
149
|
+
* id = "i-1234567890abcdef0"
|
|
150
|
+
* }
|
|
151
|
+
*
|
|
152
|
+
* check "health" {
|
|
153
|
+
* assert {
|
|
154
|
+
* condition = data.http.health.status_code == 200
|
|
155
|
+
* error_message = "Health check failed"
|
|
156
|
+
* }
|
|
157
|
+
* }
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export declare class GenericBlockParser {
|
|
161
|
+
/**
|
|
162
|
+
* Parses a generic block into a GenericBlock structure.
|
|
163
|
+
* @param block - The raw HCL block to parse
|
|
164
|
+
* @returns Parsed GenericBlock with type, labels, properties, and nested blocks
|
|
165
|
+
*/
|
|
166
|
+
parse(block: HclBlock): GenericBlock;
|
|
167
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generic parsers for Terraform blocks.
|
|
4
|
+
* Handles resource, data, provider, module, terraform settings, and unknown block types.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.GenericBlockParser = exports.DataParser = exports.ResourceParser = exports.ModuleParser = exports.ProviderParser = exports.TerraformSettingsParser = void 0;
|
|
8
|
+
const bodyParser_1 = require("../utils/parser/bodyParser");
|
|
9
|
+
const valueHelpers_1 = require("../utils/common/valueHelpers");
|
|
10
|
+
/** Meta-argument keys that are separated from resource properties */
|
|
11
|
+
const META_KEYS = new Set(['count', 'for_each', 'provider', 'depends_on', 'lifecycle']);
|
|
12
|
+
/**
|
|
13
|
+
* Parser for terraform settings blocks.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```hcl
|
|
17
|
+
* terraform {
|
|
18
|
+
* required_version = ">= 1.0.0"
|
|
19
|
+
* required_providers {
|
|
20
|
+
* aws = {
|
|
21
|
+
* source = "hashicorp/aws"
|
|
22
|
+
* version = "~> 4.0"
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
class TerraformSettingsParser {
|
|
29
|
+
/**
|
|
30
|
+
* Parses a terraform settings block.
|
|
31
|
+
* @param block - The raw HCL block to parse
|
|
32
|
+
* @returns Parsed TerraformSettingsBlock
|
|
33
|
+
*/
|
|
34
|
+
parse(block) {
|
|
35
|
+
const parsed = (0, bodyParser_1.parseBlockBody)(block.body);
|
|
36
|
+
return {
|
|
37
|
+
properties: parsed.attributes,
|
|
38
|
+
raw: block.raw,
|
|
39
|
+
source: block.source
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.TerraformSettingsParser = TerraformSettingsParser;
|
|
44
|
+
/**
|
|
45
|
+
* Parser for provider configuration blocks.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```hcl
|
|
49
|
+
* provider "aws" {
|
|
50
|
+
* alias = "west"
|
|
51
|
+
* region = "us-west-2"
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
class ProviderParser {
|
|
56
|
+
/**
|
|
57
|
+
* Parses a provider configuration block.
|
|
58
|
+
* @param block - The raw HCL block to parse
|
|
59
|
+
* @returns Parsed ProviderBlock with name, alias, and properties
|
|
60
|
+
*/
|
|
61
|
+
parse(block) {
|
|
62
|
+
const name = block.labels[0] || 'default';
|
|
63
|
+
const parsed = (0, bodyParser_1.parseBlockBody)(block.body);
|
|
64
|
+
return {
|
|
65
|
+
name,
|
|
66
|
+
alias: (0, valueHelpers_1.literalString)(parsed.attributes.alias) ?? parsed.attributes.alias?.raw,
|
|
67
|
+
properties: parsed.attributes,
|
|
68
|
+
raw: block.raw,
|
|
69
|
+
source: block.source
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.ProviderParser = ProviderParser;
|
|
74
|
+
/**
|
|
75
|
+
* Parser for module call blocks.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```hcl
|
|
79
|
+
* module "vpc" {
|
|
80
|
+
* source = "terraform-aws-modules/vpc/aws"
|
|
81
|
+
* version = "3.0.0"
|
|
82
|
+
*
|
|
83
|
+
* name = "my-vpc"
|
|
84
|
+
* cidr = "10.0.0.0/16"
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
class ModuleParser {
|
|
89
|
+
/**
|
|
90
|
+
* Parses a module call block.
|
|
91
|
+
* @param block - The raw HCL block to parse
|
|
92
|
+
* @returns Parsed ModuleBlock with name and all properties
|
|
93
|
+
*/
|
|
94
|
+
parse(block) {
|
|
95
|
+
const name = block.labels[0] || 'unnamed';
|
|
96
|
+
const parsed = (0, bodyParser_1.parseBlockBody)(block.body);
|
|
97
|
+
return {
|
|
98
|
+
name,
|
|
99
|
+
properties: parsed.attributes,
|
|
100
|
+
raw: block.raw,
|
|
101
|
+
source: block.source
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.ModuleParser = ModuleParser;
|
|
106
|
+
/**
|
|
107
|
+
* Parser for resource definition blocks.
|
|
108
|
+
* Separates meta-arguments (count, for_each, depends_on, etc.) from resource properties.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```hcl
|
|
112
|
+
* resource "aws_instance" "web" {
|
|
113
|
+
* count = 3
|
|
114
|
+
* ami = var.ami_id
|
|
115
|
+
* instance_type = "t2.micro"
|
|
116
|
+
*
|
|
117
|
+
* tags = {
|
|
118
|
+
* Name = "web-${count.index}"
|
|
119
|
+
* }
|
|
120
|
+
*
|
|
121
|
+
* dynamic "ebs_block_device" {
|
|
122
|
+
* for_each = var.ebs_volumes
|
|
123
|
+
* content {
|
|
124
|
+
* device_name = ebs_block_device.value.device_name
|
|
125
|
+
* volume_size = ebs_block_device.value.size
|
|
126
|
+
* }
|
|
127
|
+
* }
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
class ResourceParser {
|
|
132
|
+
/**
|
|
133
|
+
* Parses a resource block, separating properties, meta-arguments, and dynamic blocks.
|
|
134
|
+
* @param block - The raw HCL block to parse
|
|
135
|
+
* @returns Parsed ResourceBlock with separated concerns
|
|
136
|
+
*/
|
|
137
|
+
parse(block) {
|
|
138
|
+
const [type, name] = block.labels;
|
|
139
|
+
const parsed = (0, bodyParser_1.parseBlockBody)(block.body);
|
|
140
|
+
const meta = {};
|
|
141
|
+
const properties = {};
|
|
142
|
+
// Separate meta-arguments from properties
|
|
143
|
+
for (const [key, value] of Object.entries(parsed.attributes)) {
|
|
144
|
+
if (META_KEYS.has(key)) {
|
|
145
|
+
meta[key] = value;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
properties[key] = value;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
type: type || 'unknown',
|
|
153
|
+
name: name || 'unnamed',
|
|
154
|
+
properties,
|
|
155
|
+
blocks: parsed.blocks.filter((child) => child.type !== 'dynamic'),
|
|
156
|
+
dynamic_blocks: this.extractDynamicBlocks(parsed.blocks),
|
|
157
|
+
meta,
|
|
158
|
+
raw: block.raw,
|
|
159
|
+
source: block.source
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Extracts dynamic blocks from nested blocks.
|
|
164
|
+
* @param blocks - Nested blocks from the resource body
|
|
165
|
+
* @returns Array of DynamicBlock objects
|
|
166
|
+
*/
|
|
167
|
+
extractDynamicBlocks(blocks) {
|
|
168
|
+
const dynamicBlocks = [];
|
|
169
|
+
for (const block of blocks) {
|
|
170
|
+
if (block.type !== 'dynamic') {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const label = block.labels[0] || 'dynamic';
|
|
174
|
+
const for_each = block.attributes.for_each;
|
|
175
|
+
const iterator = (0, valueHelpers_1.literalString)(block.attributes.iterator);
|
|
176
|
+
const contentBlock = block.blocks.find((child) => child.type === 'content');
|
|
177
|
+
dynamicBlocks.push({
|
|
178
|
+
label,
|
|
179
|
+
for_each,
|
|
180
|
+
iterator,
|
|
181
|
+
content: contentBlock?.attributes || {},
|
|
182
|
+
raw: block.raw
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return dynamicBlocks;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.ResourceParser = ResourceParser;
|
|
189
|
+
/**
|
|
190
|
+
* Parser for data source blocks.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```hcl
|
|
194
|
+
* data "aws_ami" "ubuntu" {
|
|
195
|
+
* most_recent = true
|
|
196
|
+
*
|
|
197
|
+
* filter {
|
|
198
|
+
* name = "name"
|
|
199
|
+
* values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
|
|
200
|
+
* }
|
|
201
|
+
*
|
|
202
|
+
* owners = ["099720109477"]
|
|
203
|
+
* }
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
class DataParser {
|
|
207
|
+
/**
|
|
208
|
+
* Parses a data source block.
|
|
209
|
+
* @param block - The raw HCL block to parse
|
|
210
|
+
* @returns Parsed DataBlock with type, name, properties, and nested blocks
|
|
211
|
+
*/
|
|
212
|
+
parse(block) {
|
|
213
|
+
const [dataType, name] = block.labels;
|
|
214
|
+
const parsed = (0, bodyParser_1.parseBlockBody)(block.body);
|
|
215
|
+
return {
|
|
216
|
+
dataType: dataType || 'unknown',
|
|
217
|
+
name: name || 'unnamed',
|
|
218
|
+
properties: parsed.attributes,
|
|
219
|
+
blocks: parsed.blocks,
|
|
220
|
+
raw: block.raw,
|
|
221
|
+
source: block.source
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
exports.DataParser = DataParser;
|
|
226
|
+
/**
|
|
227
|
+
* Parser for generic/unknown block types.
|
|
228
|
+
* Used for moved, import, check, terraform_data, and unrecognized blocks.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```hcl
|
|
232
|
+
* moved {
|
|
233
|
+
* from = aws_instance.old
|
|
234
|
+
* to = aws_instance.new
|
|
235
|
+
* }
|
|
236
|
+
*
|
|
237
|
+
* import {
|
|
238
|
+
* to = aws_instance.example
|
|
239
|
+
* id = "i-1234567890abcdef0"
|
|
240
|
+
* }
|
|
241
|
+
*
|
|
242
|
+
* check "health" {
|
|
243
|
+
* assert {
|
|
244
|
+
* condition = data.http.health.status_code == 200
|
|
245
|
+
* error_message = "Health check failed"
|
|
246
|
+
* }
|
|
247
|
+
* }
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
class GenericBlockParser {
|
|
251
|
+
/**
|
|
252
|
+
* Parses a generic block into a GenericBlock structure.
|
|
253
|
+
* @param block - The raw HCL block to parse
|
|
254
|
+
* @returns Parsed GenericBlock with type, labels, properties, and nested blocks
|
|
255
|
+
*/
|
|
256
|
+
parse(block) {
|
|
257
|
+
const parsed = (0, bodyParser_1.parseBlockBody)(block.body);
|
|
258
|
+
return {
|
|
259
|
+
type: block.keyword,
|
|
260
|
+
labels: block.labels,
|
|
261
|
+
properties: parsed.attributes,
|
|
262
|
+
blocks: parsed.blocks,
|
|
263
|
+
raw: block.raw,
|
|
264
|
+
source: block.source
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
exports.GenericBlockParser = GenericBlockParser;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for Terraform locals blocks.
|
|
3
|
+
* Extracts local value definitions from locals blocks.
|
|
4
|
+
*/
|
|
5
|
+
import { HclBlock, LocalValue } from '../types/blocks';
|
|
6
|
+
/**
|
|
7
|
+
* Parser for Terraform locals definition blocks.
|
|
8
|
+
* Converts a single locals block into multiple LocalValue entries.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```hcl
|
|
12
|
+
* locals {
|
|
13
|
+
* environment = "production"
|
|
14
|
+
* tags = {
|
|
15
|
+
* Name = "example"
|
|
16
|
+
* Env = local.environment
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare class LocalsParser {
|
|
22
|
+
/**
|
|
23
|
+
* Parses a locals block into an array of LocalValue objects.
|
|
24
|
+
* Each attribute in the locals block becomes a separate LocalValue.
|
|
25
|
+
*
|
|
26
|
+
* @param block - The raw HCL block to parse
|
|
27
|
+
* @returns Array of LocalValue objects, one per local definition
|
|
28
|
+
*/
|
|
29
|
+
parse(block: HclBlock): LocalValue[];
|
|
30
|
+
}
|