Gulp src ignore part of glob path for recursive copy - gulp

I would like to copy some openui5 resource files into my output folder from bower packages. They have all a common prefix (openui5-). They all have a subfolder called "resources". I would like to copy that content from that subfolder to one common "resources" folder in my output.
I would like to use a glob to copy them with gulp. For now I have to explicitly give each path
['bower_components/openui5-sap.m/resources/**/*',
'bower_components/openui5-sap.ui.core/resources/**/*',
'bower_components/openui5-themelib_sap_belize/resources/**/*']
I would like to use a pattern like this:
'bower_components/openui5-*/resources/**/*'
But if I do this, I get the full module name copied, too, so my resource folder looks like this:
out/resources/
+ -- openui5-sap.m/resources/...
+ -- openui5-sap.ui.core/resources/...
+ -- openui5-themelib_sap_belize/resources/...
As I came to understand this is because per default gulp.src takes the first glob (which is in the module name) and makes the recursive structure from there.
Is there a way to ignore parts of the glob pattern for the output or trim the output paths using another glob?
I played around and searched for any solutions, but I cannot find anything.
gulp-rename seems to flatten the whole hierarchy:
gulp.task('copyui5resources', function() {
gulp.src('bower_components/openui5-*/**/*')
.pipe(rename({ dirname: '' }))
.pipe(gulp.dest('out/resources'));
});
And using the base option does not help either. It seems to copy just a part of it:
gulp.task('copyui5resources', function() {
gulp.src('bower_components/openui5-*/**/*', {base: 'bower_components/openui5-*'})
.pipe(gulp.dest('out/resources'));
});
Here is a screenshot with my input folder structure and my gulp task so far. Thank you for your help!

You're on the right track trying to use gulp-rename. However the dirname option isn't powerful enough for what you're trying to accomplish, since it can only replace the entire directory structure. You want to replace just a part of it.
For cases like these gulp-rename can be supplied with a function, which allows you to alter dirname using everything JavaScript has to offer including string.replace().
That means you can do this:
var gulp = require('gulp');
var rename = require('gulp-rename');
gulp.task('default', function() {
return gulp.src('bower_components/openui5-*/resources/**/*')
.pipe(rename(f => f.dirname = f.dirname.replace(/openui5-.*?\//, '')))
.pipe(gulp.dest('out/'));
});

Thank you for your help Sven,
I just stumbled on that hint this morning in another post before I read your post :( I removed the first two parts of the path and got the desired result. It has the advantage, that you can skip as many parts you want and the regex solution has the advantage catching even more similar path parts.
For completion: Here is the solution I came up with:
var gulp = require('gulp'),
rename = require('gulp-rename'),
path = require('path');
gulp.task('copyui5resources', function() {
gulp.src('bower_components/openui5-*/resources/**/*')
.pipe(rename(function(p) {
var nda = p.dirname.split(/[\\\/]/);
nda.splice(0, 2);
p.dirname = nda.length > 0 ? path.join.apply(null, nda) : "";
}))
.pipe(gulp.dest('out/resources'));
});
I used path to rejoin the paths indipendent from the OS. Splice removes the first two parts with the package name and the resources directory. You have to use path.join.apply, as it flattens the array, otherwise you get an error.

Related

Gulp - want to inject SCSS variable into another file

UPDATE -- See additional info below, added on 22 March...
I want to inject an SCSS variable into a code file with my Gulp process.
I think this is two steps:
Gulp the SCSS _variables.scss file so I can access the variable
Just do a simple gulp.replace to do a string-replace and drop the
variable value into my file (like the answer here)
I'm stuck on step 1. Here's what I have... as best I can tell, the not-heavily-documented gulp-sass-variables plugin is supposed to pull the variables from the SCSS file into the argv object. So I followed the example on that plugin page, and tried this:
gulp.task('test', function () {
gulp.src('sass/*')
.pipe(plugins.print())
.pipe(plugins.sassVariables({
$foundIt: argv.testSassVar
}))
console.log(argv);
});
The print() call is just to confirm that it is reading my SCSS files (and it is). But it can't find $testSassVar in my SCSS code, as you can see from this error message:
[12:53:04] Using gulpfile ~/dev/project/gulpfile.js
[12:53:04] Starting 'test'...
[12:53:04] 'test' errored after 15 ms
[12:53:04] ReferenceError: testSassVar is not defined
at Gulp.<anonymous> (...)
[12:53:04] sass/_mixins.scss <- this line and all below are from print()
[12:53:04] sass/_normalize.scss
[12:53:04] sass/_variables.scss
[12:53:04] sass/footer
[12:53:04] sass/header
[12:53:04] sass/landing
[12:53:04] sass/landing-style.scss
[12:53:04] sass/site
[12:53:04] sass/style.scss
I tried removing that $foundIt assignment, and just trying to fill the argv object, but that ends up outputting an empty array:
[13:00:57] Using gulpfile ~/dev/project/gulpfile.js
[13:00:57] Starting 'test'...
{ _: [ 'test' ], '$0': 'gulp' }
[13:00:57] Finished 'test' after 41 ms
Any ideas how I can accomplish what I'm trying to do?!
EDIT 22 March
Looks like I can partially accomplish this by going a different route and using the gulp-sass-json plugin:
gulp.task('sass-json', function () {
return gulp
.src(scss_base + '*')
.pipe(plugins.sassJson())
.pipe(gulp.dest(tmp + 'sass-vars.json'));
});
This writes a file to /tmp with my SCSS variables compiled into a JSON file. Then I thought, ok, I can load the JSON file in gulp and parse it from there. Unfortunately, this plugin doesn't resolve 2nd level variables (i.e. variables that reference variables), so you get something like this:
{
# variables parsed from SCSS to JSON by gulp-sass-json
# these are ok
"font__main": "'Open Sans', sans-serif",
"font__heading": "'Open Sans', sans-serif",
"font__line-height-body": "1.5",
"testSassVar": "123",
"color__nbar": "#ffffff",
"color__nbar_bg": "#50B107",
"size_border-radius": "3px"
# these variables reference other variables, not parsed, ouch
"color__background-body": "$color__nbar",
"color__background-screen": "$color__nbar",
# and this is even worse, the SCSS function isn't parsed, double ouch
"color_test": "lighten($color__nbar, 50%)",
}
As you can see, the variables that are simple references to other variables could probably be fixed with a bit of Javascript to just parse them, but once you get into the SASS functions, that idea falls apart.
Ideally, the SCSS compiler could parse and variables that aren't static values. Is that possible (or is that starting to really get into the weeds to solve this problem)?
The only other thought I had was to move all my variables to a JSON file, then use a gulp plugin that moved in the other direction -- JSON to SASS variables. But then I wouldn't get the advantage of having the SCSS compiler to run functions, parse variables, etc, and then give those values back to my gulp file.
Since my goal is to have the SASS values in the (parsed) variables available to my gulp file, so I can turn around and drop them into another (HTML or PHP) code file doing a simple string replace. If there's an easier way to accomplish my final goal, I'm all ears...
I recently had a similar issue, although I'm not using gulp.
I searched for npm packages that could accomplish this task (variable extraction from SCSS), but the couple I found were subpar.
So, I ended up writing my own lib (extract-scss-variables).
We use it in production, it's tested against foundation and a couple of other common uses.
I sincerely don't remember how to produce a gulp plugin anymore, but this library in itself shouldn't be hard to integrate.
In your specific case I'd do something like:
var extractScssVariables = require('extract-scss-variables');
var variables = extractScssVariables({
entryPoint: 'path/to/sass/style.scss',
files: [
'path/to/sass/_mixins.scss',
'path/to/sass/_normalize.scss',
'path/to/sass/_variables.scss',
],
sassOptions: {
includePaths: ['path/to/libraries/to/include.scss'],
},
});
where the entryPoint is the context where the variables are extracted (e.g. if you overwrite a variable declared in _mixins.scss, the entryPoint's one is used instead);
files is the array of files where to extract SCSS variables from;
and sassOptions are any command line sass options you are already using.
I've also authored a library for this kind of use case that uses native sass compiler feature to extract the variable values called sass-extract which also comes with a webpack loader and babel plugin depending on the use case.
The benefit is that is based on the native sass type system and thus supports all kinds of sass features like functions, mixins, lists, maps etc. It also handles imports, overrides, defaults etc as expected.
If you are open to using webpack you can simply import the sass file:
const style = require('sass-extract-loader!./style.scss');
to get the variables extracted, which would be the easiest option. Otherwise you can just use the core library directly as such:
const sassExtract = require('sass-extract');
sassExtract.render({
file: 'path/to/my/styles.scss'
})
.then(rendered => {
console.log(rendered.vars);
});
There is also a synchronous version available that you can use to integrate with your gulp pipelines.
Here is my solution - you need to have gulp-inject
main.scss file
...
/* inject:scss */
/* endinject */
...
gulpfile.js file
var gulpInject = require('gulp-inject');
...
gulp.src('...your/main/scss/file.scss')
.pipe(
gulpInject(gulp.src('...your/partial/scss/**/*.scss'), {
relative: true,
transform: function (filePath) {
var fileWithoutExtension = filePath.replace('.scss', '');
return '#import \'' + fileWithoutExtension + '\';';
}
})
)
.pipe(...)
...
The above example will inject partial SCSS files into the main SCSS file.
Hopefully my code would be helpful.

Gulp, css is saving to dist/css/less, should be dist/css

trying to setup my gulp file for a project, I want to compile the less, and then minify it and have it save this to a new folder.
My less file is saved in app/less/styles.less, the compiled css file should save to dist/css/styles.css but it's saving to dist/css/less/styles.css.
What am I doing wrong here?
var app = 'app/',
dist = 'dist/',
appStyles = app + '**/*.less';
gulp.task('compilecssremote', function(){
return gulp.src(appStyles)
.pipe(plumber({
errorHandler: onError
}))
.pipe(changed(dist)) //must be dist
.pipe(urladjuster({
prepend: '/' + project + '/dist/' //based on location of CSS files
}))
.pipe(less())
.pipe(minifycss({keepBreaks: false}))
.pipe(gulp.dest(dist + 'css'))
});
This is the expected behavior when you use a gulp.src like 'app/**/*.less (aka your appStyles) to match source files with paths like app/less/styles.less. There are two pieces to understand here:
By default everything before the first glob in your gulp.src path will be omitted from the output path. In this case that's everything before the **, which is to say the app/. That's why the your output css file isn't going to 'dest/app/…. (For a more detailed discussion of this, see this answer)
The path matched by the first glob and on is preserved in the output path. In this case, that's the less/ in the matched file 'app/less/styles.less' (that is, the part of 'app/**/*.less' represented by **). (It's beside the point in the case of your problem, but this feature is very useful for preserving relative file structures.)
Using #1 & #2, one quick fix would be to trim less/ from the output path by putting it before the first glob in the gulp.src. For example, change appStyles = app + 'less/**/*.less'.

How to use Webpack to combine JSON files from all subdirectories into one?

Say I have a directory structured like this:
1. folder A
1a. some_file.js
1b. data.json
2. folder B
2a. folder B1
2a1. other_file.json
2a2. data.json
2b. folder B2
2b1. data.json
3. output.json
Is there a webpack loader that can combine data.json in all subfolders, and output it to output.json?
I've found https://www.npmjs.com/package/json-files-merge-loader that seems to do something similar, but it seems to ask for each path to data.json, while I need something that goes through all folders and subfolders for data.json. All data.json keys are unique, and I want output.json to be one JSON object containing all key/value pair from all data.json.
webpack is not really suited for the use case you have. Many people think that webpack can be a replacement for a build system, but it's just a module bundler, it doesn't handle every task. Specifically:
webpack traverses require() and import statements, meaning it needs the modules to be statically defined. It's possible to get around this by writing a plugin or using a file generated from a template, but even if you did that...
webpack would have a hard time combining the JSON files in the way you want. webpack is good at bundling files into some sort of modules system (CommonJS or AMD). What you want is not a bundle containing modules, but a file containing arbitrary contents.
webpack might be able to do these things using some fancy plugins, but it's likely not worth it. In your case, you probably just want to write a Node script. If you want, you can use a plugin like this to run the code before build.
const fs = require('fs');
// https://github.com/isaacs/node-glob
const glob = require('glob');
const output = {};
glob('src/**/*.json', (error, files) => {
files.forEach((filename) => {
const contents = JSON.parse(fs.readFileSync(filename, 'utf8'));
Object.assign(output, contents);
});
fs.writeFileSync('output.json', JSON.stringify(output));
});
Is there a webpack loader that can combine data.json in all subfolders, and output it to output.json?
Use the merge-webpack-plugin.
This is differ from to run the code before build. Because deep webpack integration allow to follow file changes and update results immidiately while hot reloading:
MergePlugin = require("merge-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.(json)$/i,
use: [
MergePlugin.loader()
]
}
]
},
plugins: [
new MergePlugin({
search: './src/**/*.json',
})
]
}
if you need only one target file output.json for all folders in your project describe them all in search param, and that is all
if you need to join separated files for each top level folders (output_A.json, output_B.json ...) then:
if your do not need to lookup through subfolders try to play with group param with value [path] - read more about grouping
if you need to join all files through each folder and its subfolders you need to create multiple plugin instances for each top level folder; but you can group files in each plugin instance by name or by ext (see grouping)
and some more
#dee Please check this plugin
https://www.npmjs.com/package/merge-jsons-webpack-plugin
You can pass array of files/patterns as an input and it will emit single file as json.
My multi-json-loader might be helpful here. It accepts a glob and combines the files into a single JSON blob while also retaining relative paths in the output object.
Ie, dirA/a.json, dirB/b.json, and dirB/c.json get output as
{
"dirA": {
"a": /*parsed contents of a.json*/
},
"dirB": {
"b": /*parsed contents of b.json*/,
"c": /*parsed contents of b.json*/
}
}

How to copy files from a ** desination to another in Gulp src and dist?

I have some problem can't find a direct answer on just this one.
I have a folder structure like this:
modules/**/views/**/*.html
modules/cart/views/templates/
modules/cart/views/content/
modules/cart/views/content/foo/
I want to move them so they end up like this:
Views/templates/
Views/content/
Views/content/foo/
I have tried base: and cwd: with
{ base: 'modules/**/views/**' }
Also with . and ./ etc.. but cant get it to work with two ** I dont want the first ** just files from the second ** and it's folder
Any clue? does it even work? Tried
I found a solution by using gulp-rename.
Not sure if it's the best one but worked.
I split my path. Get the name of the first **
renamed it to empty value, same with the views.
return gulp.src(config.vSrc)
.pipe(rename(function(path) {
var splitedPath = path.dirname.split('\\');
var moduleName = splitedPath[0];
path.dirname = path.dirname.replace(moduleName, '').replace('views', '');
gutil.log(path.dirname);
}))
.pipe(gulp.dest("Views"));
To update this answer, now a good way to do this is with gulp-flatten. It is very good at extracting specific folders from the folder hierarchy. I believe in the OP's case what he wants is:
gulp.src(['config.vSrc'])
.pipe(flatten({ subPath: [3] }))
.pipe(gulp.dest('Views'));
That should get the 4th, etc. folder on down. It is possible you would need this syntax:
subPath: [3,] I am not sure about that without testing it.

Gulp globbing and negate

I'm having an issue where I'm unable to compile a set of LESS file using Globbing.
My structure is this:
+ dir3
- dir3.1
- dir3.1.1
- dir3.1.2
- dir3.2
- dir3.3
- responsive.less
- fixedwidths.less
Each dir has a set of less files and what I'm trying to do is to compile responsive.less and fixedwidths.less but i want to ignore all the remaining folders in dir3.
My Gulp Glob for compiling less files is:
var paths = {
root: 'Publication/',
source: 'Publication/LESS/',
styles: 'Publication/**/*.less'
}
// Compile less files
gulp.task('styles', function() {
gulp.src([
paths.source+'**/*.less', // compile it all
'!'+paths.source+'**/variables.less', // ignore all variables.less
paths.source+'responsive/*.less', // compile responsive & fixedwidths
'!'+paths.source+'responsive/**/*.less' // ignore all less files in subdir of dir3
])
.pipe(less())
.pipe(gulp.dest(paths.root+'build'))
});
I was expecting that paths.source+'responsive/*.less' would match responsive.less and fixedwidths.less and '!'+paths.source+'responsive/**/*.less' would ignore everything else but that's not what seems to be happening. The latter bit code seems to halt all processing of dir3.
Can anything help?
There is a bug in node-glob related to negated glob patterns. See this bower issue and this node-glob issue. Maybe your problem is related to this?
If so, this workaround should work './!('+paths.source+'responsive/**/*.less)'.