{"id":13827599,"url":"https://github.com/dimitrinicolas/lepto","last_synced_at":"2025-04-05T10:08:21.149Z","repository":{"id":52170168,"uuid":"135767863","full_name":"dimitrinicolas/lepto","owner":"dimitrinicolas","description":"Automated image Editing, Optimization and Analysis via CLI and a web interface. You give to lepto your input and output directories, the plugins you want to use and their options. Then lepto does his job, you keep your original files and the structure of the input directory. Some plugins can even collect data (like primary colors) from your images and save them in a JSON file.","archived":false,"fork":false,"pushed_at":"2021-03-08T07:58:06.000Z","size":44264,"stargazers_count":496,"open_issues_count":8,"forks_count":16,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-05-28T00:01:40.956Z","etag":null,"topics":["automation","images","lepto","nodejs","optimization"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dimitrinicolas.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-06-01T22:15:10.000Z","updated_at":"2023-12-05T16:31:47.000Z","dependencies_parsed_at":"2022-08-24T00:41:31.000Z","dependency_job_id":null,"html_url":"https://github.com/dimitrinicolas/lepto","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimitrinicolas%2Flepto","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimitrinicolas%2Flepto/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimitrinicolas%2Flepto/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimitrinicolas%2Flepto/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dimitrinicolas","download_url":"https://codeload.github.com/dimitrinicolas/lepto/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247318744,"owners_count":20919484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","images","lepto","nodejs","optimization"],"created_at":"2024-08-04T09:02:02.883Z","updated_at":"2025-04-05T10:08:21.122Z","avatar_url":"https://github.com/dimitrinicolas.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","📦 Legacy \u0026 Inactive Projects"],"sub_categories":[],"readme":"[![Lepto: The best image optimizations practices made simple](fixtures/readme/lepto.jpg)](#get-started-with-cli--npm-scripts)\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://travis-ci.org/leptojs/lepto\"\u003e\u003cimg src=\"https://travis-ci.org/leptojs/lepto.svg?branch=master\" alt=\"Build Status\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nThe main purpose of this tool is to automate image optimization and analysis. \nThis project is recent, so use it with care, I'm listening to all feedback (we \ncan talk via [twitter][twitter], don't follow me I never tweet).\n\n**What is the difference with [ImageMin][imagemin]?** I think that if you deal \nwith large applications, then go on ImageMin, but if you are building small \nstatic websites and you want to optimize your resources easily, then you could \ntry lepto.\n\nYou give to lepto your input and output directories, the plugins you want to \nuse and their options. Then lepto does his job, you keep your original files \nand the structure of the input directory. Some plugins can even collect data \n(like primary colors) from your images and save them in a JSON file.\n\nIf you want to learn more about image optimizations, I recommend to you the \namazing [images.guide][images.guide] by Addy Osmani.\n\nDesign and illustrations by [Dorian Colin][dorian colin].\n\n## Get started with CLI / NPM scripts\n\nYou can follow this Get-started dev.to article: \n[Learn How to Automate your Images Optimization Process with Lepto][article]. \n\nI recommend you to use lepto via [lepto-cli][lepto-cli], so it can easily be \nintegrated into your build process with npm scripts.\n\n```console\n$ npm i -g lepto-cli\n```\n\nThen you can follow the setup process:\n```console\n$ lepto setup\n```\nIt will guide you to create a configuration file.\n\n[![lepto-cli](fixtures/readme/lepto-cli.jpg)][lepto-cli]\n\nCheck out [lepto-cli repository][lepto-cli] for more information.\n\n\u003e See below for Node.js API usage.\n\n## GUI\n\nYou can access the GUI if you launched lepto from the CLI, by default at the \naddress `http://localhost:4490`. You can change the port with the option \n`guiPort`.\n\nThe purpose of the GUI is to add more precise quality settings to files one by \none. You can easily play with the quality slider and see the result at the same \ntime, so you can choose the most suitable option for each of your resources.\n\nYou can also edit your filters and plugins configuration thought the interface.\n\nTo save the changes and relaunch lepto's process, click on the Save button or \npress  \u003ckbd\u003e⌘S\u003c/kbd\u003e / \u003ckbd\u003eCtrl+S\u003c/kbd\u003e.\n\n![GUI tree view](fixtures/readme/gui-image.jpg)\n\n\n## Plugins\n\nYou have to say to lepto how it will process your files. So you set in your \nconfig a `filters` array containing several groups of plugins associated with \ntheir `glob`. For each image, Lepto will go through the list of filters and \ntest if the file matches each of the plugins groups. If it matches the same \nplugin several times, **the last one will overwrite the parameters of the** \n**previous ones, but the plugin will keep its place in the order of process**. \nExample:\n\n```js\n/* letpo.config.json */\n{\n  \"input\": \"assets/input\",\n  \"output\": \"assets/output\",\n  \"watch\": true,\n  \"filters\": [\n    {\n      \"glob\": \"**/*.*\",\n      \"use\": [\n        {\n          \"name\": \"lepto.jpeg\",\n          \"quality\": 80\n        }, {\n          \"name\": \"lepto-resize\",\n          \"maxWidth\": 1200\n        }\n      ]\n    }, {\n      \"glob\": \"*.jpg\",\n      \"use\": [\n        {\n          \"name\": \"lepto-resize\",\n          \"height\": 100\n        }, {\n          \"name\": \"lepto.jpeg\",\n          \"quality\": 60\n        }\n      ]\n    }\n  ]\n}\n```\nThe file `photo.jpg`, will be processed with this list of plugins:\n```js\n[\n  {\n    \"name\": \"lepto.jpeg\",\n    \"quality\": 60\n  }, {\n    \"name\": \"lepto-resize\",\n    \"height\": 100\n  }\n]\n```\nSo the first order of appearance is preserved, but the settings are overridden \nby the last ones.\n\nIf you want to use a plugin more than one time, you can add `#` at the end of \nits name, so it's creating like a \"new plugin\", eg: `\"lepto-resize#retina\"`.\n\nYou can disable a plugin by setting its `disabled` option to `true`.\n\n### Built-in plugins\n\nLepto carries some built-in plugins, their name is prefixed by `\"lepto.\"`. \nThese plugins don't create more files than they receive. Their only goal is to \noptimize files size, they can't output a larger file.\n\n#### \"lepto.jpeg\"\n\nIt uses [`sharp`][sharp]. Default config:\n```js\n{\n  \"name\": \"lepto.jpeg\",\n  \"quality\": 80, /* From 1 to 100 */\n  \"progressive\": true,\n  \"forceExt\": null /* You can force the same extension for all jpgs file, eg: replace all .jpeg images by setting forceExt to \"jpg\" */\n}\n```\n\n#### \"lepto.png\"\n\nIt uses [`node-pngquant`][pngquant]. Default config:\n```js\n{\n  \"name\": \"lepto.png\",\n  \"quality\": \"70-80\", /* From 0 (worst) to 100 (better) */\n  \"colors\": 256, /* From 2 to 256 */\n  \"speed\": 3 /* From 1 (faster but heavier) to 10 (slower but lighter) */\n}\n```\n\n#### \"lepto.gif\"\n\nIt uses ImageMin's implementation of gifsicle: [`gifsicle`][gifsicle]. Default \nconfig:\n```js\n{\n  \"name\": \"lepto.gif\",\n  \"colors\": 256 /* From 2 to 256 */\n}\n```\n\n#### \"lepto.svg\"\n\nIt uses [`svgo`][svgo], and his config follows the [SVGO's config][svgo doc].\n\n### Additional plugins\n\n* [`lepto-resize`][lepto-resize] To resize and create retina alternatives\n* [`lepto-webp`][lepto-webp] To create .webp alternatives\n* [`lepto-vibrant-color`][lepto-vibrant-color] To collect the vibrant colors \nfrom your images using `node-vibrant` and save them inside your data JSON file. \nSo you can set a **placeholder background color to your images** while they are \nloading.\n\n## Config\n\nDefault config:\n```js\n{\n  \"input\": null, /* Input directory */\n  \"output\": null, /* Output directory */\n  \"filters\": [], /* The list of filters associated with their plugins */\n\n  \"ignore\": null, /* An array or an unique glob string */\n  \"watch\": false, /* Watch for input file changes */\n  \"watchConfig\": false, /* Watch for config file change to automatically update it */\n  \"followUnlink\": false, /* Remove output files when the source file is deleted from the input directory */\n  \"processAll\": true, /* Process all files on launch, recommended if followUnlink activated */\n  \"logLevel\": \"all\", /* all 0-3 (0: silent, 1: only errors, ..., 3: all) */\n  \n  \"gui\": true, /* The GUI can be disabled */\n  \"openGui\": false, /* Automatically open the GUI in your default browser */\n  \"guiPort\": \"4490\", /* GUI port */\n\n  \"dataOutput\": null, /* Path of your data json file, eg: output/data.json */\n  \"dataRootPath\": null /* Relative path removed from file names inside the data json file */\n}\n```\n\nLepto watch files by default when launched with lepto-cli.\n\n## Node.js API\n\n```console\n$ npm i -D lepto\n```\n\nYou simply have to call `lepto()` by giving it your config.\n\n```js\nconst lepto = require('lepto');\n\nconst runner = lepto({\n  input: 'assets/input',\n  output: 'assets/output',\n  filters: [\n\n  ]\n  /* ...config */\n})\n```\n\nNow you can listen to events with the `on(event, callback)` method, the events \nare `all` for all events, `success`, `info`, `warn`, `error` and \n`processed-file`.\n\nThe `success`, `info` and `warn` events gives an object with a `msg` inside, \neg: `{ msg: 'Info message' }`.\n\nThe `error` just gives a string of the error message.\n\nThe `processed-file` gives an object with information about the file process:\n\n```js\n{\n  adj: 'new', /* file watch event: '' (initial process), 'new' or 'changed' */\n  input: 'icons/github.png',\n  inputSize: 1000000, /* sizes in bytes */\n  output: [ 'icons/github.png', 'icons/github@2x.png' ],\n  outputSizes: [ 20000, 50000 ],\n  timeSpent: 300 /* process time in ms */\n}\n```\n\nExample of events integration:\n\n```js\nrunner.on('error', msg =\u003e {\n  console.error(msg);\n});\nrunner.on('processed-file', data =\u003e {\n  /* deal with data */\n});\nrunner.on('all', (data, event) =\u003e { /* When listening to 'all' events, the callback receive the event name as a second argument */\n  if (typeof data.msg !== 'undefined') {\n    console.log(`${event}: ${data.msg}`);\n  }\n});\n```\n\n## Contributing\n\nI really like to save people time. That's why I created this tool:\n\n* To help developers easily optimize their images\n* To make sites load faster\n\nSo if you have any suggestion that could help people use this tool faster, tell \nme!\n\n### Lepto Build process\n\nRun `npm test` for testing the tool.\n\nThere is only a build step for the GUI part that can be launched with the \n`npm start` command. It will watch for CSS and js files changes from the \n`gui/src/` directory and compiles them into `gui/dist/` with Babel and PostCSS.\n\nBecause you could ask yourself the question: I love React but I didn't use it \nfor the GUI because I had planned to deal with many `contenteditable` elements \nthat are terrible to work with React. The part of the is messy I admit it, I \nhave to tidy up.\n\n### Lepto Plugin writing\n\nA lepto plugin has to deal with multiples output files associated to one input \nfile, a plugin is a function called with the plugins options that must return a \nfunction that will process the files. This last function receives an `input` \nobject, a `fulfill` method and an object of `utils` methods.\n\nThe `input` object looks like that:\n```js\ninput = {\n  input: 'icons/social/github.png',\n  outputs: [\n    {\n      dir: 'icons/social',\n      filename: 'github.png',\n      buffer: \u003cBuffer\u003e\n    },\n    {\n      dir: 'icons/social',\n      filename: 'github@2x.png',\n      buffer: \u003cBuffer\u003e\n    }\n  ],\n  data: {}\n};\n```\n\nIf the plugin is the first called, it will receive only one output, additional \noutputs are created by others plugins.\n\nA data object is shared between plugins during the process of files, his \ncontent will be saved to a JSON file chosen by the user.\n\nBecause lepto plugins have to deal with multiples outputs Buffer and often with \nan async process, I suggest you this model:\n```js\nconst namePlugin = (opts={}) =\u003e {\n  return function name(input, fulfill, utils) {\n    const next = () =\u003e {\n      finish--;\n      if (finish \u003c= 0) {\n        fulfill(input);\n      }\n    };\n\n    let finish = input.outputs.length;\n    for (const i in input.outputs) {\n      if (Object.prototype.hasOwnProperty.call(input.outputs, i)) {\n        optimizer(input.outputs[i].buffer).then(function optimizerThen(i) {\n          return function optimizerSuccess(buffer) {\n            input.outputs[i].buffer = buffer;\n            next();\n          };\n        }(i));\n      }\n    }\n  };\n};\n\nmodule.exports = namePlugin;\n```\n\nUtils functions:\n\n* `utils.size(Buffer)` return an object like `{ width: 100, height: 100 }`.\n* `utils.sharp(Buffer)` [sharp node module][sharp].\n* `utils.mime(Buffer)` return the mime type as a string, eg: `\"image/jpeg\"`, \n[learn more here][MIME doc].\n* `utils.base(String)` return the base name of a file name, eg: \n`\"IMG001.JPG\"` \u003e `\"IMG001\"`.\n* `utils.ext(String)` return the extension of a file name, eg: \n`\"IMG001.JPG\"` \u003e `\"JPG\"`.\n\nYou can inspire yourself with the [built-in plugins](plugins/).\n\n## License\n\nThis project is licensed under the [MIT license](LICENSE).\n\n[lepto-cli]: https://github.com/leptojs/lepto-cli\n[lepto-resize]: https://github.com/leptojs/lepto-resize\n[lepto-webp]: https://github.com/leptojs/lepto-webp\n[lepto-vibrant-color]: https://github.com/leptojs/lepto-vibrant-color\n\n[imagemin]: https://github.com/imagemin/imagemin\n[sharp]: https://www.npmjs.com/package/sharp\n[pngquant]: https://www.npmjs.com/package/pngquant\n[gifsicle]: https://www.npmjs.com/package/gifsicle\n[svgo]: https://www.npmjs.com/package/svgo\n[svgo doc]: https://github.com/svg/svgo#what-it-can-do\n[MIME doc]: https://developer.mozilla.org/fr/docs/Web/HTTP/Basics_of_HTTP/MIME_types\n\n[twitter]: https://twitter.com/dimitrincls\n[article]: https://dev.to/dimitrinicolas/learn-how-to-automate-your-image-optimization-process-with-lepto-890\n[dorian colin]: https://doriancolin.fr/\n[images.guide]: https://images.guide/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimitrinicolas%2Flepto","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdimitrinicolas%2Flepto","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimitrinicolas%2Flepto/lists"}