UNPKG

9.74 kBJavaScriptView Raw
1/* --------------------------------------------------------------------------------------------
2 * Copyright (c) Remy Suen. All rights reserved.
3 * Licensed under the MIT License. See License.txt in the project root for license information.
4 * ------------------------------------------------------------------------------------------ */
5'use strict';
6Object.defineProperty(exports, "__esModule", { value: true });
7exports.Dockerfile = void 0;
8const vscode_languageserver_types_1 = require("vscode-languageserver-types");
9const ast = require("./main");
10const imageTemplate_1 = require("./imageTemplate");
11const from_1 = require("./instructions/from");
12const util_1 = require("./util");
13const main_1 = require("./main");
14class Dockerfile extends imageTemplate_1.ImageTemplate {
15 constructor(document) {
16 super();
17 this.initialInstructions = new imageTemplate_1.ImageTemplate();
18 this.buildStages = [];
19 this.directives = [];
20 /**
21 * Whether a FROM instruction has been added to this Dockerfile or not.
22 */
23 this.foundFrom = false;
24 this.document = document;
25 }
26 getEscapeCharacter() {
27 for (const directive of this.directives) {
28 if (directive.getDirective() === ast.Directive.escape) {
29 const value = directive.getValue();
30 if (value === '\\' || value === '`') {
31 return value;
32 }
33 }
34 }
35 return '\\';
36 }
37 getInitialARGs() {
38 return this.initialInstructions.getARGs();
39 }
40 getContainingImage(position) {
41 let range = vscode_languageserver_types_1.Range.create(vscode_languageserver_types_1.Position.create(0, 0), this.document.positionAt(this.document.getText().length));
42 if (!util_1.Util.isInsideRange(position, range)) {
43 // not inside the document, invalid position
44 return null;
45 }
46 if (this.initialInstructions.getComments().length > 0 || this.initialInstructions.getInstructions().length > 0) {
47 if (util_1.Util.isInsideRange(position, this.initialInstructions.getRange())) {
48 return this.initialInstructions;
49 }
50 }
51 for (const buildStage of this.buildStages) {
52 if (util_1.Util.isInsideRange(position, buildStage.getRange())) {
53 return buildStage;
54 }
55 }
56 return this;
57 }
58 addInstruction(instruction) {
59 if (instruction.getKeyword() === main_1.Keyword.FROM) {
60 this.currentBuildStage = new imageTemplate_1.ImageTemplate();
61 this.buildStages.push(this.currentBuildStage);
62 this.foundFrom = true;
63 }
64 else if (!this.foundFrom) {
65 this.initialInstructions.addInstruction(instruction);
66 }
67 if (this.foundFrom) {
68 this.currentBuildStage.addInstruction(instruction);
69 }
70 super.addInstruction(instruction);
71 }
72 setDirectives(directives) {
73 this.directives = directives;
74 }
75 getDirective() {
76 return this.directives.length === 0 ? null : this.directives[0];
77 }
78 getDirectives() {
79 return this.directives;
80 }
81 resolveVariable(variable, line) {
82 for (let from of this.getFROMs()) {
83 let range = from.getRange();
84 if (range.start.line <= line && line <= range.end.line) {
85 // resolve the FROM variable against the initial ARGs
86 let initialARGs = new imageTemplate_1.ImageTemplate();
87 for (let instruction of this.initialInstructions.getARGs()) {
88 initialARGs.addInstruction(instruction);
89 }
90 return initialARGs.resolveVariable(variable, line);
91 }
92 }
93 let image = this.getContainingImage(vscode_languageserver_types_1.Position.create(line, 0));
94 if (image === null) {
95 return undefined;
96 }
97 let resolvedVariable = image.resolveVariable(variable, line);
98 if (resolvedVariable === null) {
99 // refers to an uninitialized ARG variable,
100 // try resolving it against the initial ARGs then
101 let initialARGs = new imageTemplate_1.ImageTemplate();
102 for (let instruction of this.initialInstructions.getARGs()) {
103 initialARGs.addInstruction(instruction);
104 }
105 return initialARGs.resolveVariable(variable, line);
106 }
107 return resolvedVariable;
108 }
109 getAvailableVariables(currentLine) {
110 if (this.getInstructionAt(currentLine) instanceof from_1.From) {
111 let variables = [];
112 for (let arg of this.getInitialARGs()) {
113 let property = arg.getProperty();
114 if (property) {
115 variables.push(property.getName());
116 }
117 }
118 return variables;
119 }
120 let image = this.getContainingImage(vscode_languageserver_types_1.Position.create(currentLine, 0));
121 return image ? image.getAvailableVariables(currentLine) : [];
122 }
123 getParentStage(image) {
124 const templateFrom = image.getFROM();
125 const imageName = templateFrom === null ? null : templateFrom.getImageName();
126 if (imageName === null) {
127 return null;
128 }
129 for (const from of this.getFROMs()) {
130 if (from.getBuildStage() === imageName) {
131 const range = from.getRange();
132 // on the same line then it's an image that shares the name as the build stage
133 if (range.start.line === templateFrom.getRange().start.line) {
134 return null;
135 }
136 return this.getContainingImage(range.start);
137 }
138 }
139 return null;
140 }
141 getStageHierarchy(line) {
142 const image = this.getContainingImage(vscode_languageserver_types_1.Position.create(line, 0));
143 if (image === null) {
144 return [];
145 }
146 const stages = [image];
147 let stage = this.getParentStage(image);
148 while (stage !== null) {
149 stages.splice(0, 0, stage);
150 stage = this.getParentStage(stage);
151 }
152 return stages;
153 }
154 getAvailableWorkingDirectories(line) {
155 const availableDirectories = new Set();
156 for (const image of this.getStageHierarchy(line)) {
157 for (const workdir of image.getWORKDIRs()) {
158 if (workdir.getRange().end.line < line) {
159 let directory = workdir.getAbsolutePath();
160 if (directory !== undefined && directory !== null) {
161 if (!directory.endsWith("/")) {
162 directory += "/";
163 }
164 availableDirectories.add(directory);
165 }
166 }
167 }
168 }
169 return Array.from(availableDirectories);
170 }
171 /**
172 * Internally reorganize the comments in the Dockerfile and allocate
173 * them to the relevant build stages that they belong to.
174 */
175 organizeComments() {
176 const comments = this.getComments();
177 for (let i = 0; i < comments.length; i++) {
178 if (util_1.Util.isInsideRange(comments[i].getRange().end, this.initialInstructions.getRange())) {
179 this.initialInstructions.addComment(comments[i]);
180 }
181 else {
182 for (const buildStage of this.buildStages) {
183 if (util_1.Util.isInsideRange(comments[i].getRange().start, buildStage.getRange())) {
184 buildStage.addComment(comments[i]);
185 }
186 }
187 }
188 }
189 }
190 getRange() {
191 const comments = this.getComments();
192 const instructions = this.getInstructions();
193 let range = null;
194 if (comments.length === 0) {
195 if (instructions.length > 0) {
196 range = vscode_languageserver_types_1.Range.create(instructions[0].getRange().start, instructions[instructions.length - 1].getRange().end);
197 }
198 }
199 else if (instructions.length === 0) {
200 range = vscode_languageserver_types_1.Range.create(comments[0].getRange().start, comments[comments.length - 1].getRange().end);
201 }
202 else {
203 const commentStart = comments[0].getRange().start;
204 const commentEnd = comments[comments.length - 1].getRange().end;
205 const instructionStart = instructions[0].getRange().start;
206 const instructionEnd = instructions[instructions.length - 1].getRange().end;
207 if (commentStart.line < instructionStart.line) {
208 if (commentEnd.line < instructionEnd.line) {
209 range = vscode_languageserver_types_1.Range.create(commentStart, instructionEnd);
210 }
211 range = vscode_languageserver_types_1.Range.create(commentStart, commentEnd);
212 }
213 else if (commentEnd.line < instructionEnd.line) {
214 range = vscode_languageserver_types_1.Range.create(instructionStart, instructionEnd);
215 }
216 else {
217 range = vscode_languageserver_types_1.Range.create(instructionStart, commentEnd);
218 }
219 }
220 if (range === null) {
221 if (this.directives.length === 0) {
222 return null;
223 }
224 return this.directives[0].getRange();
225 }
226 else if (this.directives.length === 0) {
227 return range;
228 }
229 return vscode_languageserver_types_1.Range.create(this.directives[0].getRange().start, range.end);
230 }
231}
232exports.Dockerfile = Dockerfile;