@@ -9,17 +9,67 @@ module.exports = function parseDockerfile(content, options = {}) {
99 includeComments : true ,
1010 ...parseOptions ,
1111 } )
12- for ( const instruction of instructions ) {
12+ const lines = content . split ( / \r ? \n / )
13+ for ( let i = 0 ; i < instructions . length ; i ++ ) {
14+ const instruction = instructions [ i ]
15+
16+ // Determine start line (0-based) from available fields; fallback to 0
17+ const startIdx =
18+ ( instruction . lineno && instruction . lineno - 1 ) ||
19+ ( instruction . startLine && instruction . startLine - 1 ) ||
20+ ( instruction . startline && instruction . startline - 1 ) ||
21+ ( instruction . line && instruction . line - 1 ) ||
22+ 0
23+
24+ // Determine next instruction start (for fallback end bound)
25+ const next = instructions [ i + 1 ]
26+ const nextStartIdx = next
27+ ? ( next . lineno && next . lineno - 1 ) ||
28+ ( next . startLine && next . startLine - 1 ) ||
29+ ( next . startline && next . startline - 1 ) ||
30+ ( next . line && next . line - 1 ) ||
31+ lines . length
32+ : lines . length
33+
34+ // End index: prefer docker-file-parser's lineno (last line of instruction), else before next instruction
35+ const endIdx =
36+ instruction . lineno && Number . isInteger ( instruction . lineno )
37+ ? instruction . lineno - 1
38+ : Math . max ( startIdx , nextStartIdx - 1 )
39+
40+ // Recover the true start by scanning upward from endIdx until the header line (instruction keyword) is found
41+ const startsWithName = ( line , name ) =>
42+ new RegExp ( `^\\s*${ name } \\b` , "i" ) . test ( line )
43+
44+ let startIdxScan = endIdx
45+ if ( instruction . name !== "COMMENT" ) {
46+ while (
47+ startIdxScan >= 0 &&
48+ ! startsWithName ( lines [ startIdxScan ] || "" , instruction . name )
49+ ) {
50+ startIdxScan --
51+ }
52+ if ( startIdxScan < 0 ) {
53+ startIdxScan = startIdx
54+ }
55+ }
56+
57+ const defaultRaw = lines . slice ( startIdxScan , endIdx + 1 ) . join ( "\n" )
58+
1359 if ( instruction . name === "COMMENT" ) {
1460 if ( removeSyntaxComments && syntaxCommentRegex . test ( instruction . args ) ) {
1561 instruction . raw = ""
1662 } else {
17- instruction . raw = instruction . args
63+ // Preserve exact original comment formatting (including indentation)
64+ instruction . raw = defaultRaw
1865 }
19- }
20- if ( instruction . name === "FROM" ) {
66+ } else if ( instruction . name === "FROM" ) {
67+ // Normalize "AS" casing but keep args semantics
2168 instruction . args = instruction . args . replace ( / ( \s + ) a s ( \s + ) / gi, "$1AS$2" )
2269 instruction . raw = generateRawInstruction ( instruction )
70+ } else {
71+ // Preserve exact original formatting for all other instructions (e.g., RUN with continuations/indentation)
72+ instruction . raw = defaultRaw
2373 }
2474 }
2575 return instructions
0 commit comments