How to manipulate by relative output path of Vinyl file inside gulp pipeline? - node.js

In the below gulp task, targetFile.relative (property of Vinyl instance)
will bee concatenated to dest path. For example if targetFile.relative is images/icons/HamburgerMenu__Icon.svg, output path will be <Project absolute path>/dest/images/icons/HamburgerMenu__Icon.svg
gulp.src('source/singletonComponents/**/*.+(png|jpg|gif|svg)')
.pipe(gulp.dest( targetVinylFile => {
return 'dest'
}));
What if we want to change targetFile.relative? For example, we want to output to dest/pictures/icons/HamburgerMenu__Icon.svg, but we don't want to rename source forlder images to pictures.
Because in this question we are considering the output path manipulation, please, not not touch source files/directories, and also 'source/singletonComponents/**/*.+(png|jpg|gif|svg)' in solutions.
And also: it you think that it's not possible, please answer such as.

With the specific example you gave, you can trivially get the effect you wan with:
gulp.src('source/singletonComponents/images/icons/**/*.+(png|jpg|gif|svg)')
.pipe(gulp.dest('dest/pictures/icons'));
The pattern above changes what is stored in the relative field of the files. They are now all relative to source/singletonComponents/images/icons/, which allows you in gulp.dest to alter the path to get the result you want.
If your real situation is more complicated you can manipulate the paths yourself through pipe:
const gulp = require("gulp");
const map = require("map-stream");
const path = require("path");
gulp.task("default", () =>
gulp.src('source/singletonComponents/**/*.+(png|jpg|gif|svg)')
.pipe(map((file, cb) => {
file.path = path.join(file.base, file.relative.replace(/images/, "pictures"));
cb(null, file);
}))
.pipe(gulp.dest('dest')));
Note that Gulp 4 does not allow modifying file.relative directly. You get an error if you try to do it. That's because it is derived from file.base and file.path. So you have to modify one of these or both if you want to change file.relative. Remember that file.path is the absolute path of the file. That's why we joint file.base to the modified value of file.relative.

Related

JSON file not found

I have a json file with the name of email_templates.json placed in the same folder as my js file bootstrap.js. when I try to read the file I get an error.
no such file or directory, open './email_templates.json'
bootstrap.js
"use strict";
const fs = require('fs');
module.exports = async () => {
const { config } = JSON.parse(fs.readFileSync('./email_templates.json'));
console.log(config);
};
email_templates.json
[
{
"name":"vla",
"subject":"test template",
"path": ""
}
]
I am using VS code , for some reason VS code doesnt autocomplete the path as well which is confusing for me.Does anyone know why it is doing this?
Node v:14*
A possible solution is to get the full path (right from C:\, for example, if you are on Windows).
To do this, you first need to import path in your code.
const path = require("path");
Next, we need to join the directory in which the JavaScript file is in and the JSON filename. To do this, we will use the code below.
const jsonPath = path.resolve(__dirname, "email_templates.json");
The resolve() function basically mixes the two paths together to make one complete, valid path.
Finally, you can use this path to pass into readFileSync().
fs.readFileSync(jsonPath);
This should help with finding the path, if the issue was that it didn't like the relative path. The absolute path may help it find the file.
In conclusion, this solution should help with finding the path.

Unable to use variables in fs functions when using brfs

I use browserify in order to be able to use require. To use fs functions with browserify i need to transform it with brfs but as far as I understood this results in only being able to input static strings as parameters inside my fs function. I want to be able to use variables for this.
I want to search for xml files in a specific directory and read them. Either by searching via text field or showing all of their data at once. In order to do this I need fs and browserify in order to require it.
const FS = require('fs')
function lookForRoom() {
let files = getFileNames()
findSearchedRoom(files)
}
function getFileNames() {
return FS.readdirSync('../data/')
}
function findSearchedRoom(files) {
const SEARCH_FIELD_ID = 'room'
let searchText = document.getElementById(SEARCH_FIELD_ID).value
files.forEach((file) => {
const SEARCHTEXT_FOUND = file.includes(searchText.toLowerCase())
if (SEARCHTEXT_FOUND) loadXML(file)
})
}
function loadXML(file) {
const XML2JS = require('xml2js')
let parser = new XML2JS.Parser()
let data = FS.readFile('../data/' + file)
console.dir(data);
}
module.exports = { lookForRoom: lookForRoom }
I want to be able to read contents out of a directory containing xml files.
Current status is that I can only do so when I provide a constant string to the fs function
The brfs README contains this gotcha:
Since brfs evaluates your source code statically, you can't use dynamic expressions that need to be evaluated at run time.
So, basically, you can't use brfs in the way you were hoping.
I want to be able to read contents out of a directory containing xml files
If by "a directory" you mean "any random directory, the name of which is determined by some form input", then that's not going to work. Browsers don't have direct access to directory contents, either locally or on a server.
You're not saying where that directory exists. If it's local (on the machine the browser is running on): I don't think there are standardized API's to do that, at all.
If it's on the server, then you need to implement an HTTP server that will accept a directory-/filename from some clientside code, and retrieve the file contents that way.

Jest - loading text files for string assertions

I am working on a text generator and I would like to compare the generated string with a text stored in a sample files. Files have indentation for some lines and it is very cumbersome to construct these strings in TS/js
Is there a simple way to load text from folder relative to current test or even project root in Jest?
Try this to import your txt into the jest file then compare with it:
const fs = require("fs");
const path = require("path");
const file = path.join(__dirname, "./", "bla.txt");
const fdr = fs.readFileSync(file, "utf8", function(err: any, data: any) {
return data;
});
expect(string).toBe(fdr)
Next to simply loading the text from a file as #avshalom showed in Jest you can also use snapshots to compare your generator output with files.
It's as simple as
it('renders correctly', () => {
const text = myGenerator.generate({...});
expect(text).toMatchSnapshot();
});
On first run the snapshot files will be written by Jest. (You then usually checkin those snapshots files) As far as i know you won't have much control over the location of the snapshot files or how to structure multiple files (other than splitting your tests across multiple test files).
If you want more control over how the files are stored and split, checkout jest-file-snapshot.

Nodejs get absolute path relative to process.cwd()

So I have the following code block:
#!/usr/bin/env node
const path = require('path');
const yargs = require('yargs').argv;
const ghpages = require('gh-pages');
const randomstring = require("randomstring");
const dirName = randomstring.generate({
length: 12,
charset: 'alphabetic'
});
console.log(__dirname, dirName, process.cwd(), yargs.directory, yargs.branch);
ghpages.publish(path.join(process.cwd(), yargs.directory), {
branch: yargs.branch,
clone: `../../../../tmp/${dirName}`
}, () => {
console.log('removing');
});
This requires an absolute path to the clone location.
Obviously I have hard coded it at the moment for testing but what I want to do is get the absolute path to /tmp/ from the process.cwd().
So basically what I want is if I ran the script in /home/otis ../../../../tmp/${dirName} would become ../../tmp/${dirName} so I need to generate the path based on the process.cwd()
Any ideas?
Cheers/
You can use path.resolve to get the absolute path.
e.g.
path.resolve('../src/tmp')
// '/Users/yourusername/src/tmp'
Or you can use the path.relative( from, to ) which gives the relative path between from and to
So in your case, I guess it is
path.relative( process.cwd(), "../../../../tmp/" )
It's bad practice to use relative paths, especially to system folders. In case if a project location will be changed, you will have to update your code as well.
If you need the system temp directory, you can use the following:
require('os').tmpdir()
It will return you correct absolute path to your temporary folder depending on current OS.

Can Gulp overwrite all src files?

Let's say I want to replace the version number in a bunch of files, many of which live in subdirectories. I will pipe the files through gulp-replace to run the regex-replace function; but I will ultimately want to overwrite all the original files.
The task might look something like this:
gulp.src([
'./bower.json',
'./package.json',
'./docs/content/data.yml',
/* ...and so on... */
])
.pipe(replace(/* ...replacement... */))
.pipe(gulp.dest(/* I DONT KNOW */);
So how can I end it so that each src file just overwrites itself, at its original location? Is there something I can pass to gulp.dest() that will do this?
I can think of two solutions:
Add an option for base to your gulp.src like so:
gulp.src([...files...], {base: './'}).pipe(...)...
This will tell gulp to preserve the entire relative path. Then pass './' into gulp.dest() to overwrite the original files. (Note: this is untested, you should make sure you have a backup in case it doesn't work.)
Use functions. Gulp's just JavaScript, so you can do this:
[...files...].forEach(function(file) {
var path = require('path');
gulp.src(file).pipe(rename(...)).pipe(gulp.dest(path.dirname(file)));
}
If you need to run these asynchronously, the first will be much easier, as you'll need to use something like event-stream.merge and map the streams into an array. It would look like
var es = require('event-stream');
...
var streams = [...files...].map(function(file) {
// the same function from above, with a return
return gulp.src(file) ...
};
return es.merge.apply(es, streams);
Tell gulp to write to the base directory of the file in question, just like so:
.pipe(
gulp.dest(function(data){
console.log("Writing to directory: " + data.base);
return data.base;
})
)
(The data argument is a vinyl file object)
The advantage of this approach is that if your have files from multiple sources each nested at different levels of the file structure, this approach allows you to overwrite each file correctly. (As apposed to set one base directory in the upstream of your pipe chain)
if you are using gulp-rename, here's another workaround:
var rename = require('gulp-rename');
...
function copyFile(source, target){
gulp.src(source)
.pipe(rename(target))
.pipe(gulp.dest("./"));
}
copyFile("src/js/app.js","dist/js/app.js");
and if you want source and target to be absolute paths,
var rename = require('gulp-rename');
...
function copyFile(source, target){
gulp.src(source.replace(__dirname,"."))
.pipe(rename(target.replace(__dirname,".")))
.pipe(gulp.dest("./"));
}
copyFile("/Users/me/Documents/Sites/app/src/js/app.js","/Users/me/Documents/Sites/app/dist/js/app.js");
I am not sure why people complicate it but by just starting your Destination path with "./" does the job.
Say path is 'dist/css' Then you would use it like this
.pipe(gulp.dest("./dist/css"));
That's it, I use this approach on everyone of my projects.

Resources