Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F91464402
ReplaceSource.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Nov 11, 09:26
Size
10 KB
Mime Type
text/x-c++
Expires
Wed, Nov 13, 09:26 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
22266824
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
ReplaceSource.js
View Options
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict"
;
const
Source
=
require
(
"./Source"
);
const
{
SourceNode
}
=
require
(
"source-map"
);
const
{
getSourceAndMap
,
getMap
,
getNode
,
getListMap
}
=
require
(
"./helpers"
);
class
Replacement
{
constructor
(
start
,
end
,
content
,
insertIndex
,
name
)
{
this
.
start
=
start
;
this
.
end
=
end
;
this
.
content
=
content
;
this
.
insertIndex
=
insertIndex
;
this
.
name
=
name
;
}
}
class
ReplaceSource
extends
Source
{
constructor
(
source
,
name
)
{
super
();
this
.
_source
=
source
;
this
.
_name
=
name
;
/** @type {Replacement[]} */
this
.
_replacements
=
[];
this
.
_isSorted
=
true
;
}
getName
()
{
return
this
.
_name
;
}
getReplacements
()
{
const
replacements
=
Array
.
from
(
this
.
_replacements
);
replacements
.
sort
((
a
,
b
)
=>
{
return
a
.
insertIndex
-
b
.
insertIndex
;
});
return
replacements
;
}
replace
(
start
,
end
,
newValue
,
name
)
{
if
(
typeof
newValue
!==
"string"
)
throw
new
Error
(
"insertion must be a string, but is a "
+
typeof
newValue
);
this
.
_replacements
.
push
(
new
Replacement
(
start
,
end
,
newValue
,
this
.
_replacements
.
length
,
name
)
);
this
.
_isSorted
=
false
;
}
insert
(
pos
,
newValue
,
name
)
{
if
(
typeof
newValue
!==
"string"
)
throw
new
Error
(
"insertion must be a string, but is a "
+
typeof
newValue
+
": "
+
newValue
);
this
.
_replacements
.
push
(
new
Replacement
(
pos
,
pos
-
1
,
newValue
,
this
.
_replacements
.
length
,
name
)
);
this
.
_isSorted
=
false
;
}
source
()
{
return
this
.
_replaceString
(
this
.
_source
.
source
());
}
map
(
options
)
{
if
(
this
.
_replacements
.
length
===
0
)
{
return
this
.
_source
.
map
(
options
);
}
return
getMap
(
this
,
options
);
}
sourceAndMap
(
options
)
{
if
(
this
.
_replacements
.
length
===
0
)
{
return
this
.
_source
.
sourceAndMap
(
options
);
}
return
getSourceAndMap
(
this
,
options
);
}
original
()
{
return
this
.
_source
;
}
_sortReplacements
()
{
if
(
this
.
_isSorted
)
return
;
this
.
_replacements
.
sort
(
function
(
a
,
b
)
{
const
diff1
=
b
.
end
-
a
.
end
;
if
(
diff1
!==
0
)
return
diff1
;
const
diff2
=
b
.
start
-
a
.
start
;
if
(
diff2
!==
0
)
return
diff2
;
return
b
.
insertIndex
-
a
.
insertIndex
;
});
this
.
_isSorted
=
true
;
}
_replaceString
(
str
)
{
if
(
typeof
str
!==
"string"
)
throw
new
Error
(
"str must be a string, but is a "
+
typeof
str
+
": "
+
str
);
this
.
_sortReplacements
();
const
result
=
[
str
];
this
.
_replacements
.
forEach
(
function
(
repl
)
{
const
remSource
=
result
.
pop
();
const
splitted1
=
this
.
_splitString
(
remSource
,
Math
.
floor
(
repl
.
end
+
1
));
const
splitted2
=
this
.
_splitString
(
splitted1
[
0
],
Math
.
floor
(
repl
.
start
));
result
.
push
(
splitted1
[
1
],
repl
.
content
,
splitted2
[
0
]);
},
this
);
// write out result array in reverse order
let
resultStr
=
""
;
for
(
let
i
=
result
.
length
-
1
;
i
>=
0
;
--
i
)
{
resultStr
+=
result
[
i
];
}
return
resultStr
;
}
node
(
options
)
{
const
node
=
getNode
(
this
.
_source
,
options
);
if
(
this
.
_replacements
.
length
===
0
)
{
return
node
;
}
this
.
_sortReplacements
();
const
replace
=
new
ReplacementEnumerator
(
this
.
_replacements
);
const
output
=
[];
let
position
=
0
;
const
sources
=
Object
.
create
(
null
);
const
sourcesInLines
=
Object
.
create
(
null
);
// We build a new list of SourceNodes in "output"
// from the original mapping data
const
result
=
new
SourceNode
();
// We need to add source contents manually
// because "walk" will not handle it
node
.
walkSourceContents
(
function
(
sourceFile
,
sourceContent
)
{
result
.
setSourceContent
(
sourceFile
,
sourceContent
);
sources
[
"$"
+
sourceFile
]
=
sourceContent
;
});
const
replaceInStringNode
=
this
.
_replaceInStringNode
.
bind
(
this
,
output
,
replace
,
function
getOriginalSource
(
mapping
)
{
const
key
=
"$"
+
mapping
.
source
;
let
lines
=
sourcesInLines
[
key
];
if
(
!
lines
)
{
const
source
=
sources
[
key
];
if
(
!
source
)
return
null
;
lines
=
source
.
split
(
"\n"
).
map
(
function
(
line
)
{
return
line
+
"\n"
;
});
sourcesInLines
[
key
]
=
lines
;
}
// line is 1-based
if
(
mapping
.
line
>
lines
.
length
)
return
null
;
const
line
=
lines
[
mapping
.
line
-
1
];
return
line
.
substr
(
mapping
.
column
);
}
);
node
.
walk
(
function
(
chunk
,
mapping
)
{
position
=
replaceInStringNode
(
chunk
,
position
,
mapping
);
});
// If any replacements occur after the end of the original file, then we append them
// directly to the end of the output
const
remaining
=
replace
.
footer
();
if
(
remaining
)
{
output
.
push
(
remaining
);
}
result
.
add
(
output
);
return
result
;
}
listMap
(
options
)
{
let
map
=
getListMap
(
this
.
_source
,
options
);
this
.
_sortReplacements
();
let
currentIndex
=
0
;
const
replacements
=
this
.
_replacements
;
let
idxReplacement
=
replacements
.
length
-
1
;
let
removeChars
=
0
;
map
=
map
.
mapGeneratedCode
(
function
(
str
)
{
const
newCurrentIndex
=
currentIndex
+
str
.
length
;
if
(
removeChars
>
str
.
length
)
{
removeChars
-=
str
.
length
;
str
=
""
;
}
else
{
if
(
removeChars
>
0
)
{
str
=
str
.
substr
(
removeChars
);
currentIndex
+=
removeChars
;
removeChars
=
0
;
}
let
finalStr
=
""
;
while
(
idxReplacement
>=
0
&&
replacements
[
idxReplacement
].
start
<
newCurrentIndex
)
{
const
repl
=
replacements
[
idxReplacement
];
const
start
=
Math
.
floor
(
repl
.
start
);
const
end
=
Math
.
floor
(
repl
.
end
+
1
);
const
before
=
str
.
substr
(
0
,
Math
.
max
(
0
,
start
-
currentIndex
));
if
(
end
<=
newCurrentIndex
)
{
const
after
=
str
.
substr
(
Math
.
max
(
0
,
end
-
currentIndex
));
finalStr
+=
before
+
repl
.
content
;
str
=
after
;
currentIndex
=
Math
.
max
(
currentIndex
,
end
);
}
else
{
finalStr
+=
before
+
repl
.
content
;
str
=
""
;
removeChars
=
end
-
newCurrentIndex
;
}
idxReplacement
--
;
}
str
=
finalStr
+
str
;
}
currentIndex
=
newCurrentIndex
;
return
str
;
});
let
extraCode
=
""
;
while
(
idxReplacement
>=
0
)
{
extraCode
+=
replacements
[
idxReplacement
].
content
;
idxReplacement
--
;
}
if
(
extraCode
)
{
map
.
add
(
extraCode
);
}
return
map
;
}
_splitString
(
str
,
position
)
{
return
position
<=
0
?
[
""
,
str
]
:
[
str
.
substr
(
0
,
position
),
str
.
substr
(
position
)];
}
_replaceInStringNode
(
output
,
replace
,
getOriginalSource
,
node
,
position
,
mapping
)
{
let
original
=
undefined
;
do
{
let
splitPosition
=
replace
.
position
-
position
;
// If multiple replaces occur in the same location then the splitPosition may be
// before the current position for the subsequent splits. Ensure it is >= 0
if
(
splitPosition
<
0
)
{
splitPosition
=
0
;
}
if
(
splitPosition
>=
node
.
length
||
replace
.
done
)
{
if
(
replace
.
emit
)
{
const
nodeEnd
=
new
SourceNode
(
mapping
.
line
,
mapping
.
column
,
mapping
.
source
,
node
,
mapping
.
name
);
output
.
push
(
nodeEnd
);
}
return
position
+
node
.
length
;
}
const
originalColumn
=
mapping
.
column
;
// Try to figure out if generated code matches original code of this segement
// If this is the case we assume that it's allowed to move mapping.column
// Because getOriginalSource can be expensive we only do it when neccessary
let
nodePart
;
if
(
splitPosition
>
0
)
{
nodePart
=
node
.
slice
(
0
,
splitPosition
);
if
(
original
===
undefined
)
{
original
=
getOriginalSource
(
mapping
);
}
if
(
original
&&
original
.
length
>=
splitPosition
&&
original
.
startsWith
(
nodePart
)
)
{
mapping
.
column
+=
splitPosition
;
original
=
original
.
substr
(
splitPosition
);
}
}
const
emit
=
replace
.
next
();
if
(
!
emit
)
{
// Stop emitting when we have found the beginning of the string to replace.
// Emit the part of the string before splitPosition
if
(
splitPosition
>
0
)
{
const
nodeStart
=
new
SourceNode
(
mapping
.
line
,
originalColumn
,
mapping
.
source
,
nodePart
,
mapping
.
name
);
output
.
push
(
nodeStart
);
}
// Emit the replacement value
if
(
replace
.
value
)
{
output
.
push
(
new
SourceNode
(
mapping
.
line
,
mapping
.
column
,
mapping
.
source
,
replace
.
value
,
mapping
.
name
||
replace
.
name
)
);
}
}
// Recurse with remainder of the string as there may be multiple replaces within a single node
node
=
node
.
substr
(
splitPosition
);
position
+=
splitPosition
;
// eslint-disable-next-line no-constant-condition
}
while
(
true
);
}
updateHash
(
hash
)
{
this
.
_sortReplacements
();
hash
.
update
(
"ReplaceSource"
);
this
.
_source
.
updateHash
(
hash
);
hash
.
update
(
this
.
_name
||
""
);
for
(
const
repl
of
this
.
_replacements
)
{
hash
.
update
(
`
$
{
repl
.
start
}
`
);
hash
.
update
(
`
$
{
repl
.
end
}
`
);
hash
.
update
(
`
$
{
repl
.
content
}
`
);
hash
.
update
(
`
$
{
repl
.
insertIndex
}
`
);
hash
.
update
(
`
$
{
repl
.
name
}
`
);
}
}
}
class
ReplacementEnumerator
{
/**
* @param {Replacement[]} replacements list of replacements
*/
constructor
(
replacements
)
{
this
.
replacements
=
replacements
||
[];
this
.
index
=
this
.
replacements
.
length
;
this
.
done
=
false
;
this
.
emit
=
false
;
// Set initial start position
this
.
next
();
}
next
()
{
if
(
this
.
done
)
return
true
;
if
(
this
.
emit
)
{
// Start point found. stop emitting. set position to find end
const
repl
=
this
.
replacements
[
this
.
index
];
const
end
=
Math
.
floor
(
repl
.
end
+
1
);
this
.
position
=
end
;
this
.
value
=
repl
.
content
;
this
.
name
=
repl
.
name
;
}
else
{
// End point found. start emitting. set position to find next start
this
.
index
--
;
if
(
this
.
index
<
0
)
{
this
.
done
=
true
;
}
else
{
const
nextRepl
=
this
.
replacements
[
this
.
index
];
const
start
=
Math
.
floor
(
nextRepl
.
start
);
this
.
position
=
start
;
}
}
if
(
this
.
position
<
0
)
this
.
position
=
0
;
this
.
emit
=
!
this
.
emit
;
return
this
.
emit
;
}
footer
()
{
if
(
!
this
.
done
&&
!
this
.
emit
)
this
.
next
();
// If we finished _replaceInNode mid emit we advance to next entry
if
(
this
.
done
)
{
return
[];
}
else
{
let
resultStr
=
""
;
for
(
let
i
=
this
.
index
;
i
>=
0
;
i
--
)
{
const
repl
=
this
.
replacements
[
i
];
// this doesn't need to handle repl.name, because in SourceMaps generated code
// without pointer to original source can't have a name
resultStr
+=
repl
.
content
;
}
return
resultStr
;
}
}
}
module
.
exports
=
ReplaceSource
;
Event Timeline
Log In to Comment