{"id":41914555,"url":"https://github.com/markdaws/versioner","last_synced_at":"2026-01-25T16:19:39.339Z","repository":{"id":7030673,"uuid":"8304849","full_name":"markdaws/versioner","owner":"markdaws","description":"A static asset versioning library for node.js","archived":false,"fork":false,"pushed_at":"2013-02-21T00:58:49.000Z","size":534,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-13T07:31:00.265Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"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/markdaws.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2013-02-20T02:57:49.000Z","updated_at":"2014-04-18T04:36:56.000Z","dependencies_parsed_at":"2022-09-11T23:22:39.880Z","dependency_job_id":null,"html_url":"https://github.com/markdaws/versioner","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/markdaws/versioner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markdaws%2Fversioner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markdaws%2Fversioner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markdaws%2Fversioner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markdaws%2Fversioner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/markdaws","download_url":"https://codeload.github.com/markdaws/versioner/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markdaws%2Fversioner/sbom","scorecard":{"id":619841,"data":{"date":"2025-08-11","repo":{"name":"github.com/markdaws/versioner","commit":"c6177ba07a51da92cb65bdf0cdfde7ce1ad5fe13"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Code-Review","score":1,"reason":"Found 1/9 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-21T04:56:33.083Z","repository_id":7030673,"created_at":"2025-08-21T04:56:33.084Z","updated_at":"2025-08-21T04:56:33.084Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28755200,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T13:59:49.818Z","status":"ssl_error","status_checked_at":"2026-01-25T13:59:33.728Z","response_time":113,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2026-01-25T16:19:38.670Z","updated_at":"2026-01-25T16:19:39.333Z","avatar_url":"https://github.com/markdaws.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# versioner\n\n##Overview\nVersioning static assets such as images, css, javascript etc. is an important part\nof any website. This node module easily allows you to add versioned assets to your\nwebsite with minimal steps, removing tedious work that is constantly repeated when\nyou want to get a website up and running in the real world.\n\nversioner uses the hash of the file contents, this means the URL of the asset will\nonly change if the contents of the file changes.  This is beneficial vs. just using\na version number since assets will only cache bust when they really change vs. every\ntime you do a new release of your website to your servers.\n\nFor example, versioner will generate URLs like:\n\n    http://localhost/assets/style.fa92255da9e98dd0b09188fb982213ec.css\n    http://localhost/assets/main.b1c7fc379fb437c54ef5981337e623b7.js\n\nInstead of:\n\n    http://localhost/assets/style-1.2.1.css\n    http://localhost/assets/main-1.2.1.js\n\nIf the contents of main.js changes but style.css doesn't the versioner approach\nwill only cause clients to redownload main.js, whereas the simple version number will\ncause all of the files to be downloaded by the client.\n\n## Installation\n`npm install versioner`\n\n## Creating a versioner instance\nIn order to create versioned assets you need to provide the location on disk where\nthe assets live, plus provide a URL where the versioned assets will be referenced\nfrom. The options that can be passed to the Versioner constructor are:\n\n```javascript\n{\n    // Determines the URL to use when creating versioned\n    // asset urls e.g. when calling imageUrl('foo.jpg') the\n    // result will be http://localhost/static/foo.\u003cHASH\u003e.jpg\n    urlRoot: 'http://localhost/static',\n\n    // Log object to provide extra debug/error logging. If true is\n    // specified a simple console logger will be used, useful for\n    // debugging. Otherwise a custom object can be specified that contains\n    // verbose(message), warning(message), error(message, errorObj) as\n    // functions.  This is useful for custom logging.\n    log: true,\n\n    // If true once the files have been versioned they will remain in memory\n    // which is useful if you want to then save the files to disk as part of\n    // a build step, or you want to serve the versioned files directly from\n    // the versioner module in your HTTP server. Defalts to false.\n    cacheFiles: true,\n\n    // Specify options for the various asset types\n    types: {\n        image: {\n            // Root directory to load images from on disk. All files will be loaded\n            // recursively from this directory\n            root: __dirname + '/images',\n\n            // OPTIONAL: overrides the global urlRoot, all image\n            // files will reference this root.\n            urlRoot: 'http://localhost/static/images',\n\n            // You can specify explicit file or provide a node Buffer instance\n            // as the source of the file.  The path is the value you will give\n            // to the url functions e.g. imageUrl, cssUrl, jsUrl to retrieve the\n            // versioned URL of the asset.\n            files: [\n                { data: img1Buffer, path: 'img1.jpg' },\n                { source: __dirname + '/images/img2.jpg', path: 'dir1/img2.jpg' }\n            ]\n        },\n        javascript: {\n            // Root directory to load javascript from\n            root: __dirname + '/javascript',\n\n            // OPTIONAL: overrides the global urlRoot, all javscript\n            // files will reference this root.\n            urlRoot: 'http://localhost/static/js',\n\n            files: [ ... ]\n        },\n        style: {\n            // Root directory to load styles from\n            root: __dirname + '/less',\n\n            // OPTIONAL: overrides the global urlRoot, all image\n            // files will reference this root.\n            urlRoot: 'http://localhost/static/css',\n\n            // Indicates the style files are less files. If nothing\n            // is specified the files are assumed to be CSS files.\n            compiler: 'less',\n\n            files: [ ... ],\n        }\n    }\n}\n```\n\n##Fetching the versioned URL of an asset\nOnce you have specified the urlRoot and sources of all the files you can use\nthe following methods on the versioner instance:\n\n```javascript\nimageUrl(path)\njsUrl(path)\ncssUrl(path)\n```\n\nIf you specify a root path, then the path you pass into the methods above is relative\nto the root path. For example, if you specify the root of images to be:\n/Users/mark/projectA/images\n\nThen to retrieve the file that lives at: /Users/mark/projectA/images/dir1/foo.jpg you\nwould call:\n\n```javascript\nversioner.imageUrl('dir1/foo.jpg')\n```\n\nThe value returned will be the versioned image name appended to the urlRoot value, so if\nurlRoot is: http://localhost/static then imageUrl('dir1/foo.jpg') will return\nhttp://localhost/static/foo.\u0026lt;UNIQUE_HASH\u0026gt;.jpg.\n\nNOTE: the path has been flattened to only use the file name, not including the dir1\nreference.\n\n##Explicit buffers\nIf you specify an explicit path for a file or specify a buffer in the files:[] property\nof the Versioner options, then to retrieve the versioned URL, you give the \"path\" value\nyou entered in the explicit file information.  For example:\n\n```javascript\nvar img1Buffer = require('fs').readFileSync('/Users/mark/projectA/images/img1.jpg');\n\nvar options = {\n    urlRoot: 'http://localhost/static',\n    types: {\n        image: {\n            files: [\n                { data: img1Buffer, path: 'dir1/img1.jpg' },\n                { source: '/Users/mark/projectA/images/img2.jpg', path: 'dir2/img2.jpg' }\n            ]\n        },\n        style: {\n            files: [\n                { source: '/Users/mark/projectA/less/url-replace.less',\n                  path: 'url-replace.css' }\n            ],\n            compiler: 'less'\n        }\n    }\n};\n```\n\nTo retrieve the versioned URL references you would call:\n```javascript\nversioner.imageUrl('dir1/img2.jpg');\n```\n\nThe reason you may have a buffer is that files in your project may not actually exist\non disk. For example, you may have files a.js, b.js and c.js on disk that you\nconcatenate together and want to serve as mysite-min.js If you wanted to version this\nyou could concatentate the files and store the concatenated data in a buffer, then pass\nthe buffer to versioner along with the path of 'mysite-min.js'\n\n##Versioning assets referenced from css/less files\nIt is very common to reference images from stylesheets. For example:\n\n```css\n.foo { background-image: url(img1.jpg); }\n```\n\nversioner provides an easy way to update the assets to point to their versioned equivalent.\nYou simply wrap the path in either versionerUrl or versionerDataUri wrappers, then when\nversioner loads the stylesheet, the image path updates e.g.\n\n```css\n.foo { background-image: url(\"versionerUrl(img1.jpg)\"); }\n.bar { background-image: url(\"versionerDataUri(img2.jpg)\"); }\n```\n\nThis will then be converted to something like:\n\n```css\n.foo { background-image: url(http://localhost/assets/img1.629f545a3f7cea350715263cd5ef3012.jpg);\n.bar { background-image: url(data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD//gA7Q=); }\n```\n\nPretty neat :)\n\n##less\nless http://lesscss.org/ support is built directly into versioner.  Simply specify the compiler\nas less in the options to the Versioner constructor and the files will automatically be compiled e.g.\n\n```javascript\n{\n    types: {\n        style: {\n            compiler: 'less'\n        }\n    }\n}\n```\n\nIf you don't specify a compiler value the style files will assume to be CSS.  You can also specify\na custom compiler to use.  Instead of passing the string 'less' specify a function with the following\nsignature:\n\n```javascript\nfunction(styleString, callback);\n\n//callback(error, compiledCss);\n```\n\nWhere styleString will be the contents of the style file and the callback should be passed the compiled\nCSS string after it has been processed.\n\nNOTE: Currently versioner does not support CSS files that reference other CSS files,\nyou can either just include multiple CSS files in your webpage or concatentate all of\nyour CSS files into a single CSS file.\n\n#Example of using versioner in your webapp\n\nBelow is a quick example of how you can us versioner in your app:\n\n```javascript\n\n// 1. Specify where to load assets from\nvar versioner = new Versioner({\n    urlRoot: 'http://localhost/assets',\n    types: {\n        image: {\n            root: __dirname + '/public/images',\n        },\n        javascript: {\n            root: __dirname + '/public/javascripts',\n        },\n        style: {\n            root: __dirname + '/public/stylesheets',\n            compiler: 'less'\n        }\n    }\n});\n\n// 2. Build the version information, then start listening for requests\nversioner.build(function(error) {\n    if(error) {\n        console.error('Failed to load versioned assets');\n        process.exit(1);\n    }\n\n    http.createServer(app).listen(port, function(){\n        console.log(\"Express server listening on port \" + port);\n    });\n});\n```\n\nIn your backend templates you can now use the versioner instance that\nyou created above, e.g. mytemplate.ejs\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003e\u003c%= title %\u003e\u003c/title\u003e\n    \u003c!-- IMPORTANT: Note how you reference the file as style.css even if\n         it was initially style.less, since after compilation the .less\n         files are now .css file --\u003e\n    \u003clink rel=\"stylesheet\" href=\"\u003c%= versioner.cssUrl('style.css') %\u003e\"/\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003e\u003c%= title %\u003e\u003c/h1\u003e\n    \u003cimg src=\"\u003c%= versioner.imageUrl('img_small.jpg') %\u003e\" /\u003e\n    \u003cscript src=\"\u003c%= versioner.jsUrl('dir1/main.js') %\u003e\" \u003e\u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nThis will end up generating something like:\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003eVersioner Example\u003c/title\u003e\n    \u003clink rel=\"stylesheet\" href=\"http://localhost/assets/style.fa92255da9e98dd0b09188fb982213ec.css\"/\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eVersioner Example\u003c/h1\u003e\n    \u003cimg src=\"http://localhost/assets/img_small.629f545a3f7cea350715263cd5ef3012.jpg\" /\u003e\n    \u003cscript src=\"http://localhost/assets/main.b1c7fc379fb437c54ef5981337e623b7.js\" \u003e\u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nThis example assumes that your server is capable of serving the versioned assets from\nthe http://localhost/assets URL root. You can do this by calling the save() method on\nthe versioner instance which will write all of the versioned assets to disk.\n\nNote: Although you can use versioner to server files directly, you should really be\nusing one of those fancy CDN things that are capable of serving large amounts of\ntraffic.\n\n##Creating versioned assets in a build step\nSimply caller the build() method then save() all of the versioned assets will\nthen be written to yhe specified output directory\n\n##Express Support\nThere is a simple to use express middleware provided as part of versioner.  This allows you to\neasily plug versioner into your existing express apps with minimal effort.\n\n```javascript\nvar Versioner = require('versioner');\n\n// 1. Create the versioner instance\nvar vers = new Versioner({ //options ... });\n\n// 2. Add the versioner middleware\napp.configure(function(){\n    // ...\n\n    // Add express middleware that will check each incoming request\n    // and see if it is a versioned asset. If it is then this piece\n    // of middleware will serve the file, otherwise the next piece\n    // of middleware will be called.\n    app.use(Versioner.Express(vers));\n\n    // Might be other assets we haven't versioned, serve those as normal\n    app.use(express.static(path.join(__dirname, 'public')));\n});\n\n// 3. Build version information and start listening for\n// requests on success\nvers.build(function(error) {\n    if(error) {\n        console.error('Failed to load versioned assets');\n        process.exit(1);\n    }\n\n    var port = app.get('port');\n    http.createServer(app).listen(port, function(){\n        console.log(\"Express server listening on port \" + port);\n    });\n});\n```\n\nFor a full working express example see examples/express/app.js\n`node examples/express/app.js`\n\nYou can then view http://localhost:5678/ and see a page that is serving\nversioned assets.\n\n##API\n```javascript\n\n/**\n * @constructor\n * Versioner wraps all of the functionality necessary to\n * version assets such as css, js and images.\n *\n * @param {Object} options\n * @param {Boolean|Object} [options.log] If set to true, a simple internal\n * logger is used that just prints verbose/warn/error messages to the console. You can\n * also specify an object that has three methods verbose(message), warn(message) and\n * error(message, error) that can be used for custom logging.\n * @param {String} options.urlRoot A string that defines the root url where assets\n * will be referenced from when using the imageUrl, cssUrl, jsUrl functions. For example\n * setting urlRoot=http://localhost/assets then calling imageUrl('foo.jpg') will\n * return the versioned asset http://localhost/assets/foo.\u003cHASH\u003e.jpg. Note: This root URL\n * can be specified for individual types if you want to server images/js/css etc from\n * different URLs (you may be setting custom headers based on paths at the nginx level)\n * @param {Object} options.types Specific type information. See Versioner constructor info\n * above for more information.\n * @param {Boolean} [options.cacheFiles=false] By default once the versioned information\n * has been processed all files are unloaded from memory.  If you set the flag to true\n * then the buffers remain in memory and you can use the versioner module to serve the\n * file contents to callers of your app.\n */\nVersioner(options)\n\n/**\n * Generates all of the versioned information.  Must be called before calling\n * any other methods on the object\n * @param {Function(Object=)} callback (error) called once all the files have been processed\n */\nbuild(callback)\n\n/**\n * Writes the versioned files to disk, you can use this in a build step\n * to pre version your assets and then serve from a static location. NOTE:\n * you must make sure you set the cacheFiles option in the Versioner constructor\n * to true if you want to use this function, otherwise it will throw an error\n *\n * @param {String} outputDir Path where files should be written to\n * @param {Function(Object=)} callback Will be called once all of the versioned\n * files have been written to disk\n */\nsave(outputDir, callback)\n\n/**\n * Given the path of an image, returns the version URL\n * @param {String} path e.g. dir1/foo.jpg\n * @return {String} The versioned URL of the resource\n */\nimageUrl(path)\n\n/**\n * Given the path of a javascript file, returns the versioned URL\n * @param {String} path e.g. dir1/foo.js\n * @return {String} The versioned URL of the resource\n */\njsUrl(path)\n\n/**\n * Given the path of a css file, returns the versioned URL\n * @param {String} path e.g. dir1/foo.css\n * @return {String} The versioned URL of the resource\n */\ncssUrl(path)\n```\n\n## Development\n```shell\ngit clone https://github.com/markdaws/versioner.git\ncd versioner\nnpm install\nnpm test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkdaws%2Fversioner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarkdaws%2Fversioner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkdaws%2Fversioner/lists"}