piqnt / svgexport

SVG to PNG/JPEG command-line tool and Node.js module
927 stars 85 forks source link

awaiting `svgexport.render` resolves before output file is added to filesystem #97

Open vegerot opened 3 years ago

vegerot commented 3 years ago

I am trying to make a script to auto-generate icon files and send them to the right spots. This is what I have so far

#!/usr/bin/env node

import * as svgexport from 'svgexport';

import * as fs from 'fs';

import { exec as execCP } from 'child_process';

import { promisify } from 'util';

const exec = promisify(execCP)

const TARGETS = {
    'android-chrome': [192, 512, 457],
    'apple-touch-icon': [60, 76, 120, 152, 180],
    favicon: [16, 32],
    'msapplication-icon': [144],
    mstile: [150],
}

async function main() {
    const args = process.argv.slice(2);
    const src = args[0] || 'src/assets/Logo.svg'
    const dest = args[1] || 'public/assets/img/icons'
    const tmp = '/tmp/my_images'
    await renderImages(TARGETS, src, tmp)
    await moveFiles(TARGETS, tmp, dest)
}

async function renderImages(/** @type {Record<string, number[]>} */ targets, src,
    /** @type {string} */ dest) {

    if (!fs.existsSync(dest)) {
        fs.mkdirSync(dest)
    }

    const sizes = new Set(Object.values(targets).flat());

    const tasks = [];

    for (const size of sizes) {
        tasks.push(
            svgexport.render({
                input: [src],
                output: [[`${dest}/${size}.png`, `${size}:`]],
                cwd: process.cwd(),
            })
        )
    }
    return Promise.all(tasks);
}

function moveFiles(/** @type {Record<string, number[]>} */ targets, /** @type{string} */ src, dest) {

    const tasks = [];
    for (const [name, sizes] of Object.entries(targets)) {
        for (const size of sizes) {
            // non-portable
            tasks.push(
                exec(`/bin/cp ${src}/${size}.png ${dest}/${name}-${size}x${size}.png`)
            );
        }
    }

    return Promise.all(tasks);
}

main();

However, I am having trouble diagnosing a bug in my code. Whenever I add a new size to TARGETS, the first time I run this script I get

rocess]. Use emitter.setMaxListeners() to increase limit
node:child_process:326
      ex = new Error('Command failed: ' + cmd + '\n' + stderr);
           ^

Error: Command failed: /bin/cp /tmp/my_images/457.png public/img/icons//android-chrome-457x457.png
cp: /tmp/my_images/457.png: No such file or directory

    at ChildProcess.exithandler (node:child_process:326:12)
    at ChildProcess.emit (node:events:369:20)
    at maybeClose (node:internal/child_process:1067:16)
    at Socket.<anonymous> (node:internal/child_process:453:11)
    at Socket.emit (node:events:369:20)
    at Pipe.<anonymous> (node:net:665:12) {
  killed: false,
  code: 1,
  signal: null,
  cmd: '/bin/cp /tmp/my_images/457.png public/img/icons//android-chrome-457x457.png',
  stdout: '',
  stderr: 'cp: /tmp/my_images/457.png: No such file or directory\n'
}

But normally after running my script a second time it works without a hitch. I suspect the bug comes from svgexport.render resolving BEFORE the files have been moved to their output locations. Then moveFiles runs before the file exists, and thus the error. This would also explain why often times running it a second time works.

My question: Is this a bug in this library, or is there something I can do to my code to fix it?

Thanks

vegerot commented 3 years ago

edit: nvm, 👇 error is just because svgexport adds event listeners for SIGINT, SIGTERM, and SIGHUP for each render job.

btw while I'm here, I also have another (probably unrelated) question. Even when the script is successful, my output looks like this

❯ ./tools/createIcons.mjs src/assets/My-Logo.svg public/img/icons/
(node:2404) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added to [process]. Use emitter.setMaxListeners() to increase limit
(Use `node --trace-warnings ...` to show where the warning was created)
(node:2404) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGINT listeners added to [process]. Use emitter.setMaxListeners() to increase limit
(node:2404) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGTERM listeners added to [process]. Use emitter.setMaxListeners() to increase limit
(node:2404) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGHUP listeners added to [process]. Use emitter.setMaxListeners() to increase limit

Which seems odd