Bundling node binaries with Parcel V2 (modern-syslog) - node.js

I need to add a node binary from modern-syslog package to the bundle (I need to bundle node modules into a single file). With Parcel V1 I used to do it with this code:
const { Asset } = require('parcel-bundler');
const path = require('path');
const URL = require('url');
class NodeAsset extends Asset {
load() {}
generate() {
const pathToAsset = this.urlJoin(
this.options.publicURL,
this.generateBundleName()
);
return [
{
type: 'js',
value: `module.exports=eval('require(${JSON.stringify(`.${pathToAsset}`)})');`
}
];
}
urlJoin(publicURL, assetPath) {
const url = URL.parse(publicURL, false, true);
const assetUrl = URL.parse(assetPath);
url.pathname = path.posix.join(url.pathname, assetUrl.pathname);
url.search = assetUrl.search;
url.hash = assetUrl.hash;
return URL.format(url);
}
}
module.exports = NodeAsset;
I tried to migrate this transformer to Parcel V2. I was able to add .node binary as an asset to the bundle with isIsolated option, but I wasn't able to do the eval('require trick to make it work. Do you have any ideas how to approach this problem?

Related

node:assert:400 throw err; ^ AssertionError [ERR_ASSERTION]: Invalid callback object specified

I'm trying to run node compile.js but it's throwing me this error and idea what I am doing wrong:
node:assert:400 throw err; ^ AssertionError [ERR_ASSERTION]: Invalid callback object specified
my inbox.sol
pragma solidity ^0.8.9;
contract Inbox{
string public message;
function Inbox(string intialMessage) public {
message = intialMessage;
}
function setMessage(string newMessage) public {
message = newMessage;
}
}
my package.json
{
"dependencies": {
"ganache-cli": "^6.12.2",
"mocha": "^9.1.3",
"solc": "^0.8.9",
"web3": "^1.6.0"
}
}
Just rewrite your code like this in 'compile.js'.This work fine even in 0.8.0 version of solidity
const path = require('path');
const fs = require('fs');
const solc = require('solc');
const inboxpath = path.resolve(__dirname, 'Contracts', 'Inbox.sol');
const source = fs.readFileSync(inboxpath, 'UTF-8');
var input = {
language: 'Solidity',
sources: {
'Inbox.sol' : {
content: source
}
},
settings: {
outputSelection: {
'*': {
'*': [ '*' ]
}
}
}
};
var output = JSON.parse(solc.compile(JSON.stringify(input)));
// console.log(output.contracts['Inbox.sol']['Inbox']);
// exports.abi = output.contracts['Inbox.sol']['Inbox'].abi;
// exports.bytecode = output.contracts['Inbox.sol']['Inbox'].evm.bytecode.object;
That course is outdated, solidity version 0.6.6 is released and you better update your code to that version. if you are not a good programmer you better refund that course, cause you will encounter many problems later on, you will see some errors using meta mask and Web3. that course teachs you a lot, so i really recommend you to keep learning that course and update yourself throughout the course. this is the first problem and the solution to the updated version is this.
this will be your "inbox.sol" code:
pragma solidity ^0.6.6;
contract Inbox{
string public message;
constructor (string memory initialMessage) public{
message = initialMessage;
}
function setMessage(string memory newMessage) public{
message = newMessage;
}
}
and this will be your "compile.js" code:
const path = require('path');
const fs = require('fs');
const solc = require('solc');
const inboxpath = path.resolve(__dirname, 'Contracts', 'Inbox.sol');
const source = fs.readFileSync(inboxpath, 'UTF-8');
var input = {
language: 'Solidity',
sources: {
'Inbox.sol' : {
content: source
}
},
settings: {
outputSelection: {
'*': {
'*': [ '*' ]
}
}
}
};
var output = JSON.parse(solc.compile(JSON.stringify(input)));
exports.abi = output.contracts['Inbox.sol']['Inbox'].abi;
exports.bytecode = output.contracts['Inbox.sol'] ['Inbox'].evm.bytecode.object;
in the new solidity the compiler will give you another version of compiled code compared to old compiler, so you'll need to pass the json file to your compiler and in order to access to abi(interface) and bytecode you need to do like i did in here.
It will work with solc version 0.4.17.
Just add memory after both string which are present in function's parameters.
function Inbox(string memory initialMessage)...
AND
function setMessage(string memory newMessage)...
For the people who are here for the v0.4.17
Simply update your package.json file solc version to 0.4.17
var output = solc.compile(source,1);
console.log(output)
I guess all you have to do is rollback your complier version. You will get your output. That worked for me.

Exclude SVG from gulp-svg-sprite build process

I'm currently building an SVG Icon system using gulp-svg-sprite and have run into a situation where I need to exclude some icons from the build process.
Is there a way to EXCLUDE an SVG from running through these 2 pipes? Somehow I need to get the src filename and compare it to the SVG I want to exclude and so something like:
if src != svgToExclude then run the pipes.
I don't want specific icons being optimized via SVGO and other plugins for those 1-off SVG's that require 2 styleable paths.
Here is the code I'm working with:
const gulp = require('gulp');
const svgo = require('gulp-svgo');
const rsp = require('remove-svg-properties').stream;
const dom = require('gulp-dom');
const xmlEdit = require('gulp-edit-xml');
const gulpIf = require('gulp-if');
const gulpIgnore = require('gulp-ignore');
const { toPath } = require('svg-points');
var excludeIcon = './utilities/checkbox-checked/checkbox-checked--s.svg';
const svgBuild = src => {
return gulp
.src(src)
.pipe(
rsp.remove({
properties: ['fill', rsp.PROPS_STROKE],
log: false,
})
)
.pipe(
svgo({
js2svg: {
indent: 2,
pretty: true,
},
plugins: [{ removeTitle: true }],
})
)
};
module.exports = svgBuild;
I'm new to gulp & node so any help would be appreciated!
Thanks,
- Ryan
Using gulp-filter:
const filter = require('gulp-filter');
something like
var excludeIconArray = ["iconToExclude1.svg", "iconToExclude2.svg", etc.]
const svgBuild = src => {
const svgFilter = filter(file => {
// will probably need file.path string manipulations here
// file.path is the full path but you can select portions of it
// so the below is just pseudocode
return !excludeIconArray.includes(file.path)
});
return gulp
.src(src)
// put the next pipe wherever you want to exclude certain files
// either right after source or just before the svgo pipe
.pipe(svgFilter())

loop in node via command line windows

I'm new to node and I want to loop trough commands in the command line using node. the commands look like:
node dist/main.js dist/index.html dynamic /
node dist/main.js dist/index.html dynamic page.html
node dist/main.js dist/index.html dynamic page2.html
I am using angular4 universal and to rerender my pages I have to put these commands in the command prompt. It wouldn't be a problem if I didn't have like 20 pages already and still more to come. My hand gets sore hehe..
How do I do this?
Thanks in regards!
the main.js file
import 'zone.js/dist/zone-node';
import { renderModuleFactory } from '#angular/platform-server'
import { enableProdMode } from '#angular/core'
import { AppServerModuleNgFactory } from './src/app.server.module.ngfactory'
import * as fs from 'fs';
import * as path from 'path';
enableProdMode();
const args = process.argv.slice(2);
if (args.length != 3) {
process.stdout.write("Usage: node dist/main.js <document> <distDir> <url>\n");
process.exit();
}
const indexFileContent = fs.readFileSync(args[0], 'utf8');
renderModuleFactory(AppServerModuleNgFactory, {
document: indexFileContent,
url: args[2]
}).then(string => {
let destUrl = args[2];
if (destUrl == '/')
destUrl = 'index.html'
const targetDir = args[1] + '/' + destUrl;
targetDir.split('/').forEach((dir, index, splits) => {
if (index !== splits.length - 1) {
const parent = splits.slice(0, index).join('/');
const dirPath = path.resolve(parent, dir);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath);
}
}
});
fs.writeFileSync(targetDir, string);
console.log(targetDir);
});
This code is from the blog: "Angular v4 Universal Demystified"
There's two ways that I know off the top of my head, exclusively using node (you could alternatively use a bash, python script)
Editing the main.js
creating a separate script.js which uses childExec
I'll assume we can edit main.js first (and update with childExec version later).
Note: I've removed non-relevant section of the code to focus on looping through the array of filenames
run with
node dist/main.js dist/index.html dynamic
main js
const args = process.argv.slice(2);
//if (args.length != 3) {
// process.stdout.write("Usage: node dist/main.js <document> <distDir> <url>\n");
// process.exit();
//}
var arr = ['page.html', 'page2.html'] //etc
arr.forEach(function(file) {
renderModuleFactory(AppServerModuleNgFactory, {
document: indexFileContent,
url: file // -> this is what we need to change page.html
}).then(string => {
let destUrl = file; // -> page.html
if (destUrl == '/')
destUrl = 'index.html'
const targetDir = args[1] + '/' + destUrl;
targetDir.split('/').forEach((dir, index, splits) => {
if (index !== splits.length - 1) {
const parent = splits.slice(0, index).join('/');
const dirPath = path.resolve(parent, dir);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath);
}
}
});
fs.writeFileSync(targetDir, string);
console.log(targetDir);
});
});
Explaination:
The script uses the format node dist/main.js <document> <distDir> <url> to render the files, as we are removing the arg[2]/<url> with the array of files in the declared arr array. This removes the need to manually type the required files.

How to reliably locate/import a dependency of a dependency?

I want to import a dependency of a dependency. For example, I want to import jade-load directly into my app:
my-app
┗━jade
┗━jade-load
I could do require('jade/node_modules/jade-load'), but this won't work if the node_modules tree has been flattened or deduped.
I thought of using require.resolve() to find out where jade-load really is, but there doesn't seem to be a way to tell it the starting point for the resolution. I need to be able to say "require jade-load from wherever jade is".
NB. I do not want to install jade-load as a direct dependency of my app; the point is to import the same instance that jade uses, so I can monkeypatch it.
I guess you may want to use proxyquire for managing dependencies of required modules. You can set proxyquire to globally override methods of the submodule when it will be loaded.
main.js
var proxyquire = require('proxyquire');
// use default childModule properties and methods unless they are redefined here
var childModuleStub = {
/* redefine here some necessary methods */
'#global': true
};
// parent module itself does't require childModule
var parentModuleStubs = {
'./childModule': childModuleStub
};
var parentModule = proxyquire('./parentModule', parentModuleStubs);
var result;
result = parentModule.exec();
console.log(result);
childModuleStub.data.sentence = "Overridden property.";
result = parentModule.exec();
console.log(result);
childModuleStub._exec = function () {
return "Overridden function.";
};
result = parentModule.exec();
console.log(result);
parentModule.js
var intermediateLibrary = require('./intermediateLibrary');
module.exports = {
exec: function() {
return intermediateLibrary.exec();
}
};
intermediateLibrary.js
var childModule = require('./childModule');
module.exports = {
exec: function() {
return childModule._exec();
}
};
childModule.js
var lib = {};
lib.data = {};
lib.data.sentence = "Hello, World!";
lib._exec = function () {
return lib.data.sentence;
};
module.exports = lib;
Results:
Hello, World!
Overridden property.
Overridden function.

Check package version at runtime in nodejs?

I have some of my entries in package.json defined as "*"
"dependencies": {
"express": "4.*",
"passport": "*",
"body-parser": "*",
"express-error-handler": "*"
},
I wan't to freeze those values to the current version. How can I know what version my packages are at run time? I don't mind checking one by one since I don't have many of them :)
BTW: I cannot do npm list --depth=0 because I cannot access the vm directly (PaaS restriction), just the logs.
You can use the fs module to read the directories in the node_modules directory and then read package.json in each of them.
var fs = require('fs');
var dirs = fs.readdirSync('node_modules');
var data = {};
dirs.forEach(function(dir) {
try{
var file = 'node_modules/' + dir + '/package.json';
var json = require(file);
var name = json.name;
var version = json.version;
data[name] = version;
}catch(err){}
});
console.debug(data['express']); //= 4.11.2
Just in case if you need the version on the front-end, there is an npm package just for this and it can be used both on client-side and server-side.
global-package-version
You can use it in your code like this
import globalPackageVersion from 'global-package-version';
// package name is 'lodash'
globalPackageVersion(require('lodash/package.json'));
// You can type 'packageVersion' in browser console to check lodash version
// => packageVersion = { lodash: '4.7.2'}
packageVersion becomes a global object when used in server side and becomes a window object when used on the client side. Works well with webpack and all other bundling tools.
Disclaimer: I am the author of this package :)
I've 'modernised' a bit #laggingreflex answer, this works on ES6+, node 10, tested on a lambda running in aws. It's an endpoint from an express app.
const fs = require("fs");
module.exports.dependencies = async (_req, res) => {
const dirs = fs.readdirSync("./node_modules");
const modulesInfo = dirs.reduce((acc, dir) => {
try {
const file = `${dir}/package.json`;
const { name, version } = require(file);
return { ...acc, [name]: version };
} catch (err) {}
}, {});
res.status(200).json(modulesInfo);
};
The accepted solution can be improved upon in both terms of performance and stability:
1: the package name IS THE directory. In typically cases where you are looking for a specific package, you do not need to load every module.
2: this code will not run on all os due to the way the paths are formed
3: using require means the path needs to be relative to the current file (this would only work if your file is located at the top of your project folder & along side node_modules). In most cases, using readFile or readFileSync is a easier approach.
const fs = require('fs');
const path = require('path');
const dirs = fs.readdirSync('node_modules');
const data = {};
//add ones you care about
const trackedPackages = ['express', 'passport', 'body-parser'];
dirs.forEach(function(dir) {
if(trackedPackages.indexOf(dir) > -1){
try{
const json = JSON.parse(
fs.readFileSync(path.join('node_modules', dir, 'package.json'), 'utf8')
);
data[dir] = json.version;
}catch(e){
console.log(`failed to read/parse package.json for ${dir}`, e);
}
}
});
console.debug(data['express']); //= 4.11.2

Resources