UNPKG

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