Add hash of most recent git commit to footer of pdf via markdown-pdf - node.js

I am using markdown-pdf via gulp to convert .md files to .pdf. I want to get the sha of the latest git commit and add it to the footer. I can get the hash like so in my gulpfile.js (found the answer):
revision = require('child_process')
.execSync('git rev-parse HEAD')
.toString().trim();
But how can I get that into my footer?
Below is my code for markdown-pdf that I am using in my gulpfile.js:
function docsToPdf() {
return src(["Views/Documentation/Files/*.md", "!Views/Documentation/Files/_README.md"])
.pipe(markdownPdf({
preProcessMd: preProcessMd,
remarkable: {
html: true
},
paperBorder: "1cm",
runningsPath: "Content/Pdf/assets/js/runnings.js",
cssPath: "Content/Pdf/assets/css/pdf.min.css"
}))
.pipe(dest("Content/Pdf"))
}
And my runnings.js file:
module.exports = {
header: {
height: '2cm',
contents: function (pageNum) {
if (pageNum == 1) {
return '<header class="pdf-header" style="padding-bottom: 20px;"><h2 style="text-align:center;margin:0;">Documentation</h2></header>'
}
return ''
}
},
footer: {
height: '1.5cm',
contents: function (pageNum, numPages) {
return '<footer class="pdf-footer" style="padding-top:20px;"><p style="float:left;width:33.33%;margin:0;font-size:10px;">' + new Date().toDateString() + '</p><p style="float:left;width:33.33%;margin:0;font-size:10px;text-align:center;">© 2020</p><p style="float:right;width:33.33%;margin:0;font-size:10px;text-align:right;">Page ' + pageNum + ' of ' + numPages + '</p></footer>'
}
}
}
And my preProccessMd:
function preProcessMd() {
var splitter = split()
var docsUrl = "https://example.org/docs/";
var urlOne = /\[\[((?:(?!\[\[).)*?)\|(.*?)]]/g;
var urlImg = /(\()(images)/g;
var replacer = through(function (data, path) {
this.queue(
data
.replace(urlOne, (_, x, y) => `[${x}](${docsUrl}${y.replace(/(?!^)[A-Z]/g, '-$&').toLowerCase()})`)
.replace(urlImg, "$1$2".replace("$2", "content/images/docs"))
+ "\n"
)
})
splitter.pipe(replacer)
return duplexer(splitter, replacer)
}

The best way to do this would be to use the preProcessMd method to inject the SHA into the footer. You do seem to have the preprocessor defined in preProcessMd: preProcessMd,. Is that actually doing anything? If so, what is the definition?
EDIT, after update from OP: Quick and dirty solution would be to use an empty span where you want the SHA to go. Then look for that span and replace with the SHA in your preProcessMd. For e.g., <span class="git-hash"></span>. Then replace <span class="git-hash"> with <span>YOUR-SHA.
You might need to update your gulp task like so:
function docsToPdf() {
revision = require('child_process')
.execSync('git rev-parse HEAD')
.toString().trim();
return src(["Views/Documentation/Files/*.md", "!Views/Documentation/Files/_README.md"])
.pipe(markdownPdf({
preProcessMd: preProcessMd.bind(this, revision),
remarkable: {
html: true
},
paperBorder: "1cm",
runningsPath: "Content/Pdf/assets/js/runnings.js",
cssPath: "Content/Pdf/assets/css/pdf.min.css"
}))
.pipe(dest("Content/Pdf"))
}
And your function preProcessMd() { to function preProcessMd(revision) {
NOTE: I'm unsure if the preProcessMd.bind(this, ... is going to be problematic or not.

Related

Vue3/Jest - Line break problem during a test

I tried to simulate a mouseover in my test, but i have a problem when i use .contain in an expect. The test doesn't pass because of the page render.
Here the result of the test:
Expected substring: "<div id=\"title\"><!--v-if--></div>"
Received string: "<div id=\"title\">
<!--v-if-->
</div>"
Here my code:
describe('mouse event', function() {
test('over change', async (done) => {
const Component = defineComponent({
template: '<div id="title" #mouseover="hoveredIcon"><span v-if="hovered">Hello World</span></div>',
data() {
return {
hovered: false,
}
},
methods: {
hoveredIcon() {
this.hovered = true
},
}
})
const wrapper = mount(Component)
expect(wrapper.html()).toContain('"<div id=\"title\"><!--v-if--></div>"')
wrapper.find("#title").trigger("mouseover");
wrapper.vm.$nextTick( () => {
expect(wrapper.html()).toContain('<div id=\"title\"><span>Hello World!</span></div>')
done();
});
})
})
How can i get the received string on a single line? Or how to made the expect part in few line to match perfectly?
expect(wrapper.html()).toContain('<div id="title"><!--v-if--></div>')
to
expect(wrapper.html()).toContain('<div id="title">
<!--v-if-->
</div>')
Any better solutions?
Thanks for your help
One solution found was to use directly \n directly in the value expected.
expect(wrapper.html()).toContain('\n \n')

htmlparser2 how to replace a tag by another custom tag with the same attributes that has the tag one

I need to change a tag for another tag with the same properties. For example change this:
<TagOne width="500" height="200">asdfasdf</TagOne>
to this:
<AnotherTag width="500" height="200">sometext</AnotherTag>
With this code I can see the attributes but i don't know how to replace the tag for the other one:
const htmlparser2 = require("htmlparser2");
const DomUtils = require("htmlparser2").DomUtils;
const htmlContent = `<html>
<head></head> <body> <div id="content">
<TagOne width="500" height="200" src="image1.jpg">asdfasdf</TagOne>
<p>asdfasdf</p> </div></body></html>`; const parser = new htmlparser2.Parser(
{
onopentag(name, attribs) {
if (name === "TagOne") {
if(attribs.width ){
var width = attribs.width;
}
if(attribs.height ){
var height = attribs.height;
}
var new_tag = `<AnotherTag width:`+width+`; height:`+height+`;">sometext</div>`;
}
},
ontext(text) {
console.log("-->", text);
},
onclosetag(tagname,new_tag) {
if (tagname === "amp-iframe") {
console.log("That's it?!");
}
},
},
{ decodeEntities: true } );
var content = parser.write(htmlContent);
parser.end();
I have tried to do this on the onclosetag function:
//htmlparser2.DomUtils.removeElement(tagname);
//parser.write(new_tag);
but it gives me an error, it is not the correct way but I can't find anything similar in the documentation. can someone help me? Thanks.

get data from nodejs to frontend js for google maps but stuck in retrieving it looking for ways to solve it

My NodeJS GET route:
router.get('/stores', function (req, res, next) {
errorMsg = req.flash('error')[0];
successMsg = req.flash('success')[0];
Product.find(function (err, products) {
// console.log(products)
res.render('admin/stores', {
layout: 'admin-map.hbs',
stores: products,
errorMsg: errorMsg,
GOOGLE_APIKEY: process.env.GOOGLE_APIKEY,
noErrors: 1
});
});
The route /stores returns json data which holds latitude and longitude and I want it in my script tag of map.html with loop, to render the pins on the map. Below, the script:
<!DOCTYPE html>
<html>
<head>
<script src = "https://maps.googleapis.com/maps/api/js"></script>
<script>
function loadMap() {
alert(this.stores);
var mapOptions = {
center:new google.maps.LatLng(17.433053, 78.412172),
zoom:5
}
var map = new google.maps.Map(document.getElementById("sample"),mapOptions);
var marker = new google.maps.Marker({
position: new google.maps.LatLng(17.433053, 78.412172),
map: map,
draggable:true,
icon:'/scripts/img/logo-footer.png'
});
marker.setMap(map);
var infowindow = new google.maps.InfoWindow({ });
infowindow.open(map,marker);
}
</script>
<!-- ... -->
</head>
</html>
How can I do it?
It seems you need to follow two steps
1. Pass data from hbs to script
Using triple brackets syntax
<script>
let stores = {{{ stores }}}; // the triple brackets
console.log('Data : ', stores);
function loadMap() {
...
Check if data is being printed in console? If yes your data is available in the front-end script and you can
2. Loop through it
...
for (let i = 0; i < stores.length; i++) {
// the JS loop instead of hbs one, because we are on front-end
var marker = new google.maps.Marker({
position: new google.maps.LatLng(stores[i].lat, stores[i].lng), // whatever applies
map: map,
draggable:true,
icon:'/scripts/img/logo-footer.png'
});
}
And donot need to call setMap(), you have already set the map in map: map above
the answer is inside fronted script i can have an object declared globally and this double flower brackets works fine with them
<script>
function loadMap() {
alert(this.stores);
var mapOptions = {
center:new google.maps.LatLng(17.433053, 78.412172),
zoom:5
}
var map = new google.maps.Map(document.getElementById("sample"),mapOptions);
{{#each stores}}
var marker = new google.maps.Marker({
position: new google.maps.LatLng(17.433053, 78.412172),
map: map,
draggable:true,
icon:'/scripts/img/logo-footer.png'
});
{{/each}}
marker.setMap(map);
var infowindow = new google.maps.InfoWindow({ });
infowindow.open(map,marker);
}
</script>

Phantomjs node set header,footer

How can I set page header and footer using phantomjs with node, basically I'm generating pdf from html and I'm willing to add my header and footer from node, I have tries with following but pdf is not showing any data, and I'm reading empty page and willing to add header and footer, here is my Code:
// Load ejs template
fs.readFile(__dirname + '/../pdf' + pdfpath, 'utf8', function (err, data) {
// Render the page to variable.
var html = ejs.render(data, pdfJSON);
// Set this html as the content for the pdf file.
page.set('content', html);
page.set('generatePDF', function (pageNum, numPages) {
if (pageNum == 1) {
return "";
}
return "<h1>Header <span style='float:right'>" + pageNum + " / " + numPages + "</span></h1>";
});
page.set('paperSize', {
width: 1200,
height: 1500,
header: {
height: "1cm",
contents: phantom.generatePDF
}
});
console.log(phantom.generatePDF);//return undifned
page.set('paperSize', {
width: 1200,
height: 1500
});
// Generate the pdf.
var fileName = __dirname + '/pdfdata/' + f.formType + f.formId + '.pdf';
page.render(fileName, cb);
});
How I can resolve this?
Take a look to these example:
https://github.com/ariya/phantomjs/blob/master/examples/printheaderfooter.js
Edit: Sorry, didn't noticed you asked for Node, you can find some solutions and answers here also:
Footer's contents don't seem to work

AngularJS : How to say to a directive to clone scope?

I have this fiddle, and can not make this work. I believe that the reason resides in that two li elements with a custom directive edit-in-place share scope.
The solution would be to say to the directive to create a copy of the scope that binds on the parent - can transclude help?
angular.module('bla', [])
.directive('editInPlace', ['$parse','$compile', function($parse, $compile) {
return {
restrict: 'A',
scope: true,
link: function (scope, element, attribs) {
var inputStart = '<input style="border: 2 solid black" name="inPlaceInput" style="display:none" value="';
var inputEnd = '">';
scope.editModeAccessor = $parse(attribs.editInPlace);
scope.modelAccessor = $parse(attribs.ngBind);
scope.$watch(attribs.editInPlace, function(newValue, oldValue){
if (newValue){
console.debug("click");
console.debug("value: " + scope.modelAccessor(scope));
var inputHtml = inputStart + scope.modelAccessor(scope) + inputEnd;
element.after(inputHtml);
jQuery(element).hide();
scope.inputElement = jQuery("input[name=inPlaceInput]");
scope.inputElement.show();
scope.inputElement.focus();
scope.inputElement.bind("blur", function() {
blur();
});
} else {
blur();
}
});
function blur(){
console.debug("blur secondary");
if (scope.inputElement){
console.debug("blur secondary inputElement found");
var value = scope.inputElement.val();
console.debug("input value: "+ value);
scope.inputElement.remove();
jQuery(element).show();
scope.editModeAccessor.assign(scope, false);
scope.modelAccessor.assign(scope, value);
}
}
}
}
}]);
function ContactsCtrl($scope, $timeout){
$scope.contacts = [{number:'+25480989333', name:'sharon'},{number:'+42079872232', name:''}];
$scope.editMode = false;
var editedId;
$scope.edit = function(id){
$scope.editMode = true;
jQuery("#"+id).hide();
editedId = id;
//TODO show delete button
}
$scope.$watch('editMode', function(newValue, oldValue){
if (!newValue && editedId){
jQuery("#"+editedId).show();
}
});
}
<div ng-app="bla">
<div ng-controller="ContactsCtrl">
<h4>Contacts</h4>
<ul>
<li ng-repeat="contact in contacts">
<span edit-in-place="editMode" ng-bind="contact.number"></span>
<span edit-in-place="editMode" ng-bind="contact.name"></span>
<span id="{{$index}}" ng-click="edit($index)"><i class="icon-edit">CLICKtoEDIT</i></span>
</li>
</ul>
</div></div>
I think cloning the scope is not the best solution.
When creating a directive in angular, you should encapsulate all the functionality within the directive. You should also avoid mixing jQuery in when you don't have to. Most of the time (as in this case) you're just introducing unnecessary complexity. Lastly, classes are the best way of controlling display, rather than the style attribute on an element.
I took the liberty of rewriting your directive in a more "angular" way - with no jQuery. As you can see from the updated jsFiddle, it is simpler and cleaner. Also, it works!
This directive can be easily modified to add lots of additional awesome functionality.
app.directive( 'editInPlace', function() {
return {
restrict: 'E',
scope: { value: '=' },
template: '<span ng-click="edit()" ng-bind="value"></span><input ng-model="value"></input>',
link: function ( $scope, element, attrs ) {
// Let's get a reference to the input element, as we'll want to reference it.
var inputElement = angular.element( element.children()[1] );
// This directive should have a set class so we can style it.
element.addClass( 'edit-in-place' );
// Initially, we're not editing.
$scope.editing = false;
// ng-click handler to activate edit-in-place
$scope.edit = function () {
$scope.editing = true;
// We control display through a class on the directive itself. See the CSS.
element.addClass( 'active' );
// And we must focus the element.
// `angular.element()` provides a chainable array, like jQuery so to access a native DOM function,
// we have to reference the first element in the array.
inputElement[0].focus();
};
// When we leave the input, we're done editing.
inputElement.prop( 'onblur', function() {
$scope.editing = false;
element.removeClass( 'active' );
});
}
};
});

Resources