Gulp 4 - Error: write after end - node.js

I'm getting an error when I watch for changes in index.html (full path in CONFIG.APP.INDEX). All my tasks are in separate files. this is tasks/watch.ts, for example:
import * as CONFIG from '../config';
export default done => {
// other watches
gulp.watch(CONFIG.APP.INDEX, gulp.series('inject'));
};
on first change task is executed normally, but on second change I'm getting this error:
c:\~\node_modules\through2\node_modules\readable-stream\lib\_stream_writable.js:203
var er = new Error('write after end');
^
Error: write after end
at writeAfterEnd (c:\~\node_modules\through2\node_modules\readable-stream\lib\_stream_writable.js:203:12)
at DestroyableTransform.Writable.write (c:\~\node_modules\through2\node_modules\readable-stream\lib\_stream_writable.js:239:20)
at DestroyableTransform.ondata (c:\~\node_modules\through2\node_modules\readable-stream\lib\_stream_readable.js:531:20)
at emitOne (events.js:77:13)
at DestroyableTransform.emit (events.js:169:7)
at readableAddChunk (c:\~\node_modules\through2\node_modules\readable-stream\lib\_stream_readable.js:198:18)
at DestroyableTransform.Readable.push (c:\~\node_modules\through2\node_modules\readable-stream\lib\_stream_readable.js:157:10)
at DestroyableTransform.Transform.push (c:\~\node_modules\through2\node_modules\readable-stream\lib\_stream_transform.js:123:32)
at afterTransform (c:\~\node_modules\through2\node_modules\readable-stream\lib\_stream_transform.js:79:51)
at TransformState.afterTransform (c:\~\node_modules\through2\node_modules\readable-stream\lib\_stream_transform.js:58:12)
at c:\~\node_modules\vinyl-fs\lib\src\getContents\bufferFile.js:18:5
at c:\~\node_modules\graceful-fs\graceful-fs.js:78:16
at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:404:3)
tasks/inject.ts task:
declare var require;
const gulp = require('gulp');
const plugins = require('gulp-load-plugins')();
import * as CONFIG from '../config';
export default done => {
return gulp
.src(CONFIG.APP.INDEX)
.pipe(require('../util/inject/fixes').default) // <--- PROBLEM IS HERE
// other stuff...
.pipe(gulp.dest(CONFIG.DST.BUILD))
.on('error', plugins.util.log);
};
util/inject/fixes.ts task
declare var require;
const plugins = require('gulp-load-plugins')();
// errors even with this...
export default plugins.util.noop();
Tasks are loaded from gulpfile.ts/index.ts like this:
fs.readdirSync('./gulpfile.ts/tasks').map(file => {
let name = file.replace(/\.ts$/, '');
let task = require(path.join(path.resolve('.'), 'gulpfile.ts', 'tasks', file));
gulp.task(name, task.default);
});
I've managed to identify where the error comes from, but no idea what's causing it, or how to fix it. Problem only occurs when watching index.html after first change and task execution. Running task manually works normally (gulp inject), and all other watches and tasks work normally.

I suspect that your implementation of fixes.ts is reusing the same noop() result instead of recreating the result each time the task is run.
Try converting the fixes.ts to return a factory function that will return a new noop instance each time the task is invoked.
util/inject/fixes.ts:
declare var require;
const plugins = require('gulp-load-plugins')();
// Return a function that will return a new `noop` instance each time:
export default () => {
return plugins.util.noop();
};
For context, I just had a similar problem in my project where I was accidentally reusing a gulp stream and getting the "write after end" error.
I suspect your code is doing the same sort of thing with the noop() result because that one value will be cached as the value of that module.
Incorrect version of my gulpfile.js - the result of my gulp.dest() calls was being reused each time.
let gulp = require('gulp');
let merge = require('merge-stream');
let _path = require('path');
let files = {
'html': {
src: _path.join('public', 'index.html'),
dest: gulp.dest('public') // <-- WRONG use of `gulp.dest()`, causes result to be reused and gives "write after end" error
},
'files': 'html': {
src: _path.join('files', '*'),
dest: gulp.dest('files') // <-- WRONG use of`gulp.dest()`
},
};
gulp.task('copy', function(){
let html = gulp.src(files.html.src)
.pipe(files.html.dest); // <-- `gulp.dest()` should be here
let files = gulp.src(files.files.src)
.pipe(files.files.dest); // <-- `gulp.dest()` should be here
return merge(html, files);
});
gulp.task('copy-watch', function(){
let srcList = Object.keys(files).map(i => files[i].src);
gulp.watch(srcList, ['copy']);
});
Fixed version of my gulpfile.js - gulp.dest() is being called each time the task is run:
let files = {
'html': {
src: _path.join('public', 'index.html'),
dest: 'public' // <-- removed `gulp.dest()` call from here
},
'files': 'html': {
src: _path.join('files', '*'),
dest: 'files' // <-- removed `gulp.dest()` call from here
},
};
gulp.task('copy', function(){
let html = gulp.src(files.html.src)
.pipe(gulp.dest(files.html.dest)); // <-- CORRECT use of`gulp.dest()`
let files = gulp.src(files.files.src)
.pipe(gulp.dest(files.files.dest)); // <-- CORRECT use of `gulp.dest()`
return merge(html, files);
});

Related

How can I include a code from another file commands

I am using this code, and what I want is for the commands to come from another file to make the index.js cleaner
const tmi = require('tmi.js');
const client = new tmi.Client({
options: { debug: true },
channels: [ 'my_name' ]
});
client.connect();
client.on('message', (channel, tags, message, self) => {
// Ignore echoed messages.
if(self) return;
if(message.toLowerCase() === '!hello') {
// "#alca, heya!"
client.say(channel, `#${tags.username}, heya!`);
}
});
In the index include the code
// index.js
// ======
var commands = require('./commands');
const tmi = require('tmi.js');
var commands = require('./commands');
const client = new tmi.Client({
options: { debug: true },
channels: [ 'my_name' ]
});
client.connect();
client.on('message', (channel, tags, message, self) => {
// Ignore echoed messages.
if(self) return;
// I replace the command with the file
commands();
});
I have the commands file structured in this way.
// commands.js
// ========
module.exports = function (channel, tags, message, self) {
if(message.toLowerCase() === '!hello') {
// "#alca, heya!"
client.say(channel, `#${tags.username}, heya!`);
}
}
But it doesn't work for me, I don't know what I'm doing wrong.
I have republished it from a new account, it has not allowed me to access the one with which I made the query. And it does not allow me to vouch for the lack of reputation. How can I include a code from another file
Responding to #jabaa's answer.
The problem you have when typing !hello should give the answer Heya! which would be client.say(channel, #${tags.username}, heya!);
#Dave Newton the intention is only to load the information from commands.js so that the index is cleaner if I use the direct code it works but when I use Commands(); It doesn't make any response.
Assuming you have 2 files in the same directory
/foo/bar/commands.js
/foo/bar/main.js
You can define commands.js like this:
exports = module.exports = {
someFunction,
anotherFunction,
}
function someFunction(text) {
console.log(`in someFunction: ${text}`);
}
function anotherFunction(text) {
console.log(`in anotherFunction(): ${text}`);
}
And then you can pull that into main.js like so:
const cmds = require('./commands');
cmds.someFunction('hello, there!');
cmds.anotherFunctio('and again!');
and get the following written to the console:
in someFunction(): hello, there!
in anotherFunction(): and again!
If you set the default export as the function itself,
exports = modules.exports = someFunction(text) {
console.log(`in someFunction(): ${text}`);
}
Then what's returned from the require() is that function:
const foo = require('./commands.js');
foo('hello, there!'):
and running that you get:
in someFunction(): hello, there!

node require resolves to original extension after clearing cache and deleting file

My code is executing const cfg = require('./config') so that either config.js or config.json will be read. In general this works fine. The problem I'm running into is during testing.
The testing code iterates on these steps:
clear the require cache for require.resolve('./config.json')
write config.json with data to be tested
test the code containing const cfg = require('./config')
Everything above works fine. The problem comes when the test tries to verify that config.js works as well.
clear the require cache for require.resolve('./config.json')
delete config.json
write config.js
test the code containing const cfg = require('./config')
The attempt to read config.js via require('./config') fails because require.resolve('./config') still resolves to the .json file which no longer exists.
The behavior could be explained by the directory being cached somewhere so that writing config.js is not reflected in node's require search. But that's just a guess.
Reversing the order of the tests so that the .js tests are performed first followed by the .json tests results in the same problem but reversed.
The behavior is the same on node versions 6+; I haven't tested earlier versions.
How can node's require be made to return the export of the .js file in this scenario?
The following illustrates the problem. By specifying the extension in the require statement (change const withExt = false to true) it succeeds.
'use strict'
/* eslint-disable no-console */
const fs = require('fs');
const fullConfigName = `${process.cwd()}/xyzzy`;
function cleanup (ext) {
// remove any files created with the default names
try {fs.unlinkSync(`${fullConfigName}.${ext}`)} catch (e) {
console.log(`failed to cleanup ${ext}`, e.code || e.message);
}
// clear the require cache
delete require.cache[`${fullConfigName}.${ext}`];
}
function writeFile (extension) {
let config;
if (extension === 'json') {
config = JSON.stringify({some: 'i was json'}) + '\n';
} else {
config = 'module.exports = {someSetting: "i was a module"}\n'
}
fs.writeFileSync(`${fullConfigName}.${extension}`, config, 'utf8');
}
function main () {
cleanup('js');
cleanup('json');
//const configName = fullConfigName;
const configName = './xyzzy';
const withExt = false;
const exts = false ? ['json', 'js'] : ['js', 'json'];
exts.forEach(ext => {
const extPart = withExt ? `.${ext}` : '';
console.log(`writing ${configName}.${ext}`);
writeFile(ext);
const fileToRequire = `${configName}${extPart}`;
console.log(`requiring ${fileToRequire}`);
const resolved = require.resolve(fileToRequire);
console.log(`require.resolve result ${resolved}`);
try {
const contents = require(`${fileToRequire}`);
console.log('contents', contents);
} catch (e) {
console.log('require failed', e.code || e.message);
}
console.log(module.children.map(c => {
return {
id: c.id,
exports: c.exports,
filename: c.filename,
loaded: c.loaded,
}
}));
const cache = Object.keys(require.cache).filter(k => true || k.startsWith(fullConfigName));
console.log('cache:', cache);
console.log(`${ext} - cleanup`);
cleanup(ext);
})
}
main ()

Create javascript resource file from local files

I'm using gulp and I trying to create a gulp task that combine files in a javascript file.
For example, image I have this:
File template\template1.html :
<h2>some html content</h2>
<p>blah blah blah</p>
File template\template2.html :
<h2>some other html content</h2>
<img src='cat.png'>
I'd like to read and merge these files into a single javascript file like this :
const templates = {
"template1" : "<h2>some html content</h2>\n<p>blah blah blah</p>",
"template2" : "<h2>some other html content</h2>\n<img src='cat.png'>"
}
export default templates
However, I'm failing when dealing with gulp plumbing (I'm quite new to gulp I admit).
How to reach my goal ?
Right now I tried to play with gulp-trough, but it fails at execution:
const gulp = require('gulp');
const through = require('gulp-through');
gulp.task('templates', function () {
var result = {}
gulp.src('src/templates/**/*.html')
.pipe(through('readFile', function(){
console.log(arguments); // not reached!
}, defaults));
})
gulp.task('default', ['templates'])
It shouldn't be hard to write your own plugin using through2 module (as explained in official docs.)
// gulpfile.js
const gulp = require('gulp');
const path = require('path');
const through = require('through2'); // npm install --save-dev through2
const toTemplateModule = obj => {
return [
`const template = ${JSON.stringify(obj, null, 2)};`,
'',
'export default template;'
].join('\n');
};
const mergeTemplate = file => {
const results = {};
let latestFile;
return through.obj(
function(file, encoding, callback) {
latestFile = file;
results[path.basename(file.path)] = file.contents.toString(encoding);
callback(null, file);
},
function(callback) {
const joinedFile = latestFile.clone({
contents: false
});
joinedFile.path = path.join(latestFile.base, file);
joinedFile.contents = Buffer.from(toTemplateModule(results), 'utf-8');
this.push(joinedFile);
callback();
});
};
gulp.task('templates', () => {
return gulp
.src('./src/templates/**/*.html')
.pipe(mergeTemplate('templates.js'))
.pipe(gulp.dest('./build'))
});
gulp.task('default', ['templates'])

How to load contents of an external file into gulp browser-sync

I am loading browser-sync proxy and want to load search and replace terms from an external file in order to amend the page as it is loaded into a browser.
The reason I want to load the search and replace terms from a separate file is because I want to make use of gulp-watch and reload browser-sync as the search and replace terms are updated.
My Project folder:
regex/search.txt <- search term is stored in this file
regex/replace.txt <- replace term is stored in this file
gulpfile.js
Contents of gulpfile.js:
var gulp = require('gulp'),
fs = require("fs"),
browserSync = require('browser-sync');
var proj_url = "http://www.example.com";
var search_text = "";
var replace_text = "";
gulp.task('readRegEx', function() {
return gulp.src('regex/*.txt')
.pipe(fs.readFile("regex/search.txt", "utf-8", function(err, data) {
search_text = data;
}))
.pipe(fs.readFile("regex/replace.txt", "utf-8", function(err, data) {
replace_text = data;
}))
});
gulp.task('browser-sync', function() {
browserSync({
proxy: {
target: proj_url
},
rewriteRules: [
{
match: search_text,
fn: function (match) {
return replace_text;
}
}
]
});
});
gulp.task('default', ['readRegEx','browser-sync'], function() {
gulp.watch(['regex/*.txt'], [browserSync.reload]);
});
This doesn't work. I get the following error:
TypeError: Cannot call method 'on' of undefined ...
For that to work you need to make browser-sync dependant in readRegEx
gulp.task('browser-sync', ['readRegEx'], function() {
this guarantees the proper execution order.
then you can make readRegEx sync (and simpler) this way:
gulp.task('readRegEx', function() {
search_text = fs.readFileSync("regex/search.txt", "utf-8").toString();
replace_text = fs.readFileSync("regex/replace.txt", "utf-8").toString();
});

Using gulp-watch with babel.js

Below is a Gulp ES6 transpilation task. It works fine, but I'm trying to replace gulp.watch with the gulp-watch plugin so new files will be caught. The problem is that gulp-watch isn't giving me what gulp.watch does in the callback, and I'm not sure what to do about it.
Here's my original working task:
var gulp = require('gulp'),
rename = require('gulp-rename'),
plumber = require('gulp-plumber'),
gprint = require('gulp-print'),
notify = require('gulp-notify'),
babel = require('gulp-babel');
gulp.task('default', function() {
return gulp.watch('../**/**-es6.js', function(obj){
if (obj.type === 'changed') {
gulp.src(obj.path, { base: './' })
.pipe(plumber({
errorHandler: function (error) { /* elided */ }
}))
.pipe(babel())
.pipe(rename(function (path) {
path.basename = path.basename.replace(/-es6$/, '');
}))
.pipe(gulp.dest(''))
.pipe(gprint(function(filePath){ return "File processed: " + filePath; }));
}
});
});
And here's all that I have so far with gulp-watch:
var gulp = require('gulp'),
rename = require('gulp-rename'),
plumber = require('gulp-plumber'),
gprint = require('gulp-print'),
notify = require('gulp-notify'),
babel = require('gulp-babel'),
gWatch = require('gulp-watch');
gulp.task('default', function() {
return gWatch('../**/**-es6.js', function(obj){
console.log('watch event - ', Object.keys(obj).join(','));
console.log('watch event - ', obj.event);
console.log('watch event - ', obj.base);
return;
if (obj.type === 'changed') {
gulp.src(obj.path, { base: './' })
.pipe(plumber({
errorHandler: function (error) { /* elided */ }
}))
.pipe(babel())
.pipe(rename(function (path) {
path.basename = path.basename.replace(/-es6$/, '');
}))
.pipe(gulp.dest(''))
.pipe(gprint(function(filePath){ return "File processed: " + filePath; }));
}
});
});
The output of the logging is this:
watch event - history,cwd,base,stat,_contents,event
watch event - change
watch event - ..
How do I get gulp-watch to give me the info I had before, or, how can I change my task's code to get this working again with gulp-watch?
According to the tests, obj.relative should contain the relative filename, and obj.path will still hold the absolute file path, just as it did in your original code. Also, the callback accepts a Vinyl object, which is documented here: https://github.com/wearefractal/vinyl
You probably can't see them in your logs since Object.keys doesn't enumerate properties in the prototype chain.
Using a for..in loop, you should be able to see all the properties.

Resources