I am trying to use Gulp4 API and writing a very basic and simple tasks to start with and am having some issues with splitting up the gulp file.
Basically I have two standalone tasks, one for minifying CSS and another for minifying JS which I am trying to import into the main gulpfile.js and calling them as part of the default task.
Below is a sample of one of the standalone gulp tasks which mainly cleans up minified css and minifies and concats the css again:
"use strict";
const { src, dest, series, task } = require( 'gulp' );
const concat = require( 'gulp-concat' );
const prefix = require( 'gulp-autoprefixer' );
const cleanCSS = require( 'gulp-clean-css' );
const rimraf = require( 'gulp-rimraf' );
const baseURL = './src/main/resources/';
const minifiedCssSources = [ baseURL + '**/*min.css' ];
const cssSources = [ baseURL + '**/*.css' ];
module.exports = function() {
const _cleanupMinifiedCss = function() {
return src(minifiedCssSources
, { allowEmpty: true }
, { read: false })
.pipe(rimraf({ force: true }));
}
const _minifyConcatCss = function() {
return src(cssSources)
.pipe(concat('css.min.css'))
.pipe(cleanCSS())
.pipe(prefix('last 2 versions'))
.pipe(dest(baseURL + 'css/'));
}
task("cssMinify", series(_cleanupMinifiedCss, _minifyConcatCss))
}
And below is a sample of my main gulpfile.js:
"use strict";
const { task, parallel } = require( 'gulp' );
const jsMinify = require("./gulp/tasks/minifyJs");
const cssMinify = require("./gulp/tasks/minifyStyles");
function defaultTask(done) {
parallel(cssMinify, jsMinify)
done();
}
task('default', defaultTask);
The issue I am having is that the default task is starting and finishing fine, however no css is being cleaned up or minified/concatenated. It's like the standalone tasks are being ignored completely.
I have been trying various ways of exporting and importing the standalone tasks however haven't managed to get this working. Unfortunately the documentation on their website is pretty minimal: "Each task can be split into its own file, then imported into your gulpfile for composition. Not only does this keep things organized, but it allows you to test each task independently or vary composition based on conditions."
Anyone has any ideas what I could try or maybe I'm doing wrong with Gulp4 API?
For the benefit of everyone, I seem to have found a way to do this after a number of tries! :)
Pasting an example below. The key is in how you export the task in your standalone task (last 2 lines).
Standalone task - say gulp\css.js
"use strict";
const { src, dest, series, task } = require( 'gulp' );
const concat = require( 'gulp-concat' );
const prefix = require( 'gulp-autoprefixer' );
const cleanCSS = require( 'gulp-clean-css' );
const rimraf = require( 'gulp-rimraf' );
const baseURL = './src/main/resources/';
const minifiedCssSources = [ baseURL + '**/*min.css' ];
const cssSources = [ baseURL + '**/*.css' ];
function _cleanupMinifiedCss() {
return src(minifiedCssSources
, { allowEmpty: true }
, { read: false })
.pipe(rimraf({ force: true }));
}
function _minifyConcatCss() {
return src(cssSources)
.pipe(concat('css.min.css'))
.pipe(cleanCSS())
.pipe(prefix('last 2 versions'))
.pipe(dest(baseURL + 'css/'));
}
const cssTasks = series(_cleanupMinifiedCss, _minifyConcatCss);
exports.cssTasks = cssTasks;
Main gulp file:
"use strict";
const { task, parallel } = require( 'gulp' );
const jsMinify = require("./gulp/tasks/minifyJs");
const cssMinify = require("./gulp/tasks/minifyStyles");
task('default', parallel(jsMinify.jsTasks, cssMinify.cssTasks));
Related
I have a react app where I wanted to import a javascript file from a third-party library but file is mark with shebang #!/usr/bin/env node.
I found (e.g. here How to Configure Webpack with Shebang Loader to Ignore Hashbang Importing Cesium React Component into Typescript React Component) I can load file by overriding webpack configuration and adding a new loader shebang-loader (I also have tried shebang-loader2) but overriding webpack in react app is recommended only with #craco/craco so I added it to package.json and tried add loader to existing webpack-config.js.
I produced this lines of code. File craco.config.js:
const throwError = (message) =>
throwUnexpectedConfigError({
packageName: 'craco',
githubRepo: 'gsoft-inc/craco',
message,
githubIssueQuery: 'webpack',
});
module.exports = {
webpack: {
configure: (webpackConfig, {paths}) => {
const shebangLoader = { test: /node_modules\/npm-groovy-lint\/lib\/groovy-lint.js$/, loader: "shebang-loader" }
const {isAdded: shebangLoaderIsAdded1} = addAfterLoader(webpackConfig, loaderByName('url-loader'), shebangLoader);
if (!shebangLoaderIsAdded1) throwError('failed to add shebang-loader');
return webpackConfig;
},
},
};
It resolves problem with shebang and it ignores #!/usr/bin/env node but now I still get error
Module parse failed: Unexpected token (14:16)
File was processed with these loaders:
* ./node_modules/shebang2-loader/index.js
You may need an additional loader to handle the result of these loaders.
| const { getSourceLines, isErrorInLogLevelScope } = require("./utils");
| class NpmGroovyLint {
> "use strict";
| options = {}; // NpmGroovyLint options
| args = []; // Command line arguments
It looks like it does not recognise "use strict" line.
Can anyone put some suggestions what should be a problem ?
After few hours of investigation, I have finally come to a resolution. Firstly I have to say that there is no option to use NpmGroovyLint in react-like applications that run in browsers because after I resolved mentioned problem up here I figured that NpmGroovyLint uses node libraries as perf_hooks which are not available in a browser enviroment.
But I can post code that resolves the problem described in my question. It was needed to add a plugin to babel-loader named 'plugin-proposal-class-properties'. Here is my snipped of craco config. You can use it as a recipe occasionally.
const {addAfterLoader, getLoaders, loaderByName, removeLoaders, throwUnexpectedConfigError} = require('#craco/craco');
const throwError = (message) =>
throwUnexpectedConfigError({
packageName: 'craco',
githubRepo: 'gsoft-inc/craco',
message,
githubIssueQuery: 'webpack',
});
module.exports = {
webpack: {
configure: (webpackConfig, {paths}) => {
const {hasFoundAny, matches} = getLoaders(webpackConfig, loaderByName('babel-loader'));
if (!hasFoundAny) throwError('failed to find babel-loader');
const {hasRemovedAny, removedCount} = removeLoaders(webpackConfig, loaderByName('babel-loader'));
if (!hasRemovedAny) throwError('no babel-loader to remove');
if (removedCount !== 2) throwError('had expected to remove 2 babel loader instances');
//add plugin proposal class properties to existing babel loader
const propClassOptions = {...matches[1].loader.options, ...{plugins: ["#babel/plugin-proposal-class-properties"]}};
const propClassLoader = {...matches[1].loader, ...{options: propClassOptions}};
const babelLoaderWithPropClassPlugin = {...matches[1], ...{loader: propClassLoader}};
const shebangLoader = {
test: /node_modules\/npm-groovy-lint\/lib\/groovy-lint.js$/,
use: [{loader: 'shebang2-loader'}, {...{loader: require.resolve('babel-loader')}, ...{options: propClassOptions}}]
}
const {isAdded: babelLoaderWithPropClassIsAdded} = addAfterLoader(webpackConfig, loaderByName('url-loader'), matches[0].loader);
if (!babelLoaderWithPropClassIsAdded) throwError('failed to add ts-loader');
const {isAdded: babelLoaderIsAdded} = addAfterLoader(webpackConfig, loaderByName('babel-loader'), babelLoaderWithPropClassPlugin.loader);
if (!babelLoaderIsAdded) throwError('failed to add back babel-loader for non-application JS');
const {isAdded: shebangLoaderIsAdded1} = addAfterLoader(webpackConfig, loaderByName('url-loader'), shebangLoader);
if (!shebangLoaderIsAdded1) throwError('failed to add shebang-loader');
return webpackConfig;
},
},
};
I have a script which generates a file, call it auto.js. This file contains some dynamically generated imports and is being used within a VueJS project.
// auto.js
import { apple, strawberry, orange } from 'delicious-fruits';
import { carrot, cucumber, celery } from 'delicious-vegetables';
While using Webpacks dev server, should any project file change, my goal is to have this script re-generate my auto.js file, and then have that included in the re-compiled project.
I have turned this script into a Webpack plugin, whereby I'm listening for the watchRun compiler hook. This seems like the ideal hook, per its description:
Executes a plugin during watch mode after a new compilation is triggered but before the compilation is actually started.
class AutoGenerate {
constructor(options) {
this.options = options;
}
apply(compiler) {
compiler.hooks.watchRun.tap('AutoGenerate', () => {
generateFile()
})
}
}
function generateFile () {
// generate auto.js and write to disk
}
I always wind up with an infinite loop situation. I have tried approaching the problem by using various life cycles events (hooks), as well ignoring the auto-generated file. Of course, by ignoring it, those changes are not included in the re-compiled project.
const webpack = require('webpack');
const AutoGenerate = require("./auto.plugin");
module.exports = {
configureWebpack: {
plugins: [
new webpack.WatchIgnorePlugin([/.*auto\.js/]),
new AutoGenerate()
]
}
}
I've also tried tapping into the compilation, and adding a new asset to the compilation. While the process does not error out, the generated asset is not a part of the final compilation.
// auto.plugin.js
class AutoGenerate {
static defaultOptions = {
outputFile: 'auto.js',
};
constructor(options = {}) {
this.options = { ...AutoGenerate.defaultOptions, ...options };
}
apply(compiler) {
compiler.hooks.thisCompilation.tap('AutoGenerate', (compilation) => {
const path = require("path")
const filePath = path.resolve(__dirname, `src/plugins/${this.options.outputFile}`)
const { RawSource } = require('webpack-sources')
const fileContent = new RawSource(generateFile())
compilation.emitAsset(
filePath,
fileContent
)
});
}
}
function generateFile() {
// generate file content & return as string
}
module.exports = { AutoGenerate };
// vue.config.js
const AutoGenerate = require("./auto.plugin");
module.exports = {
configureWebpack: {
plugins: [
new AutoGenerate()
]
}
}
How can I trigger my logic for auto-generating this file, while having this file be included as part of any re-compilation, while at the same time avoiding an infinite loop?
I have not been able to identify a direct solution to the problem posed above. However, for anyone reading, I've come to discover that this can be accomplished by utilizing a package called before-build-webpack, notably by including the watch-run trigger.
// vue.config.js
const WebpackBeforeBuildPlugin = require('before-build-webpack')
module.exports = {
configureWebpack: {
plugins: [
new WebpackBeforeBuildPlugin(function(stats, callback) {
// ...
}, ['run', 'watch-run'])
]
}
}
I don't even know how to explain my problem shortly in title. I want to create task which will follow these steps:
Take Sass File as a source (Ex. src/scss/main.scss)
Sort css properties with 'postcss-sorting'
Overwrite source file to same path (Ex. src/scss/main.scss)
Take sorted scss file and compile to css
Write to css folder (Ex. dist/css)
I try to write this task but when i start to watch scss file changes, task having infinite loop. I tried with 2 different tasks too (one for sort css properties and write file, other one for compile and write) it still looping. I don't want to sort only css properties in css file, i want to sort my scss file too. That is my example code:
// Load plugins
const {gulp, watch, src, dest, series} = require( 'gulp' ),
browserSync = require('browser-sync').create(),
postcss = require( 'gulp-postcss' ),
sortProperties = require( 'postcss-sorting' ),
sassParser = require( 'postcss-scss' ),
sass = require( 'gulp-sass' );
// Load CSS property orders for 'postcss-sorting'
const propertySortOrder = require('./propertySortOrder');
// Style
const style = () => {
return src('src/scss/**/*.scss')
.pipe(
postcss([sortProperties(propertySortOrder)], {
syntax: sassParser,
})
)
.pipe(dest('src/scss'))
.pipe(sass().on('error', sass.logError))
.pipe(dest('src/css'))
.pipe(browserSync.stream());
}
// Start Server
const startServer = (done) => {
browserSync.init({
server: { baseDir: './' },
injectChanges: true
});
done();
}
// Watch File Changes
const watchFileChanges = (done) => {
watch('src/scss/**/*.scss', style);
done();
}
exports.start = series(startServer, watchFileChanges);
Can anyone help me to fix this? Thanks
P.S. Sorry for my all grammatical mistakes, English is not my native language.
You can use gulp-changed, it will override the file only if there is a change, preventing the infinite loop:
const changed = require('gulp-changed');
const clone = require( 'gulp-clone' );
const merge = require('merge-stream');
// Style
const style = () => {
const sortedCssPipe = src('src/scss/**/*.scss')
.pipe(
postcss([sortProperties(propertySortOrder)], {
syntax: sassParser,
})
);
const scssPipe = sortedCssPipe
.pipe(clone())
.pipe(changed('src/scss', {hasChanged: changed.compareContents}))
.pipe(dest('src/scss'));
const cssPipe = sortedCssPipe
.pipe(sass().on('error', sass.logError))
.pipe(dest('src/css'))
.pipe(browserSync.stream());
return merge(scssPipe, cssPipe);
}
I am having a difficult time getting gulp.dest to output anything anywhere. I specifically want to just output to the root of the project (I'm hijacking an ASP.NET Core project for TypeScript development only). I'd really appreciate some help on how I can get dest to do what I want. Here's my current gulpfile.js:
/// <binding AfterBuild="build" Clean="clean" ProjectOpened="watch" />
"use strict";
const del = require("del"),
gulp = require("gulp"),
gulpConcat = require("gulp-concat"),
gulpRename = require("gulp-rename"),
gulpTerser = require("gulp-terser");
gulp.task("concatenate", () => {
return gulp.src([
"*.js",
"!gulpfile.js"
])
.pipe(gulpConcat("concatenated.js"))
.pipe(gulp.dest("/"));
});
gulp.task("minify", () => {
return gulp.src("concatenated.js")
.pipe(gulpTerser())
.pipe(gulpRename("min.js"))
.pipe(gulp.dest("/"));
});
gulp.task("clean", () => del([
"*.js",
"!gulpfile.js"
]));
gulp.task("build", gulp.series(
"concatenate",
"minify"
));
gulp.task("watch", () => {
gulp.watch([
"*.ts"
], gulp.series(
"build"
));
});
I think
.pipe(gulp.dest('.'))
is all you want.
I am trying to mock require using Jest because I am working in a plugin system for electron that will also use NPM for resolving dependencies.
I need to then be able to mock require inside nodejs to be able to test something similar to the next logic.
const load = (installPath, pluginsName, extensionPoint) => {
require.main.paths.push(`${installPath}/node_modules`)
pluginsName.forEach(pluginName => {
let plugin = require(pluginName)
plugin.onLoad(extensionPoint)
});
}
module.exports = { load }
Is there any simple way of doing this?. As require is not a global, is there is any other way than wrapping and injecting it to test the logic?
As explained in this answer, it's possible to replace require in particular module by evaluating it with custom require, similarly to how Node does this internally:
const Module = require('module');
const vm = require('vm');
const childModuleAbsPath = path.resolve('./foo/bar.js');
const childModuleBody = fs.readFileSync(childModuleAbsPath);
const childModuleObj = { exports: {} };
const { dir: childModuleDirname, base: childModuleFilename } = path.parse(childModuleAbsPath);
const childRequire = jest.fn().mockReturnValue(...);
vm.runInThisContext(Module.wrap(childModuleBody))(
childModuleObj.exports,
childRequire,
childModuleObj,
childModuleDirname,
childModuleFilename
);