{"id":16793595,"url":"https://github.com/mdp/bluemonday-js","last_synced_at":"2025-03-22T01:30:53.074Z","repository":{"id":66279358,"uuid":"45588220","full_name":"mdp/bluemonday-js","owner":"mdp","description":"The bluemonday sanitizer compiled to JavaScript","archived":false,"fork":false,"pushed_at":"2015-11-06T15:02:50.000Z","size":164,"stargazers_count":38,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-18T06:51:27.672Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-11-05T04:46:07.000Z","updated_at":"2022-09-05T15:17:14.000Z","dependencies_parsed_at":"2023-03-13T20:30:11.669Z","dependency_job_id":null,"html_url":"https://github.com/mdp/bluemonday-js","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdp%2Fbluemonday-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdp%2Fbluemonday-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdp%2Fbluemonday-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdp%2Fbluemonday-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdp","download_url":"https://codeload.github.com/mdp/bluemonday-js/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244893359,"owners_count":20527580,"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":[],"created_at":"2024-10-13T08:49:43.913Z","updated_at":"2025-03-22T01:30:52.681Z","avatar_url":"https://github.com/mdp.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bluemonday-js\n![Travis status](https://travis-ci.org/mdp/bluemonday-js.svg)\n\nA golang -\u003e javascript compiled version of the excellent [bluemonday](https://github.com/microcosm-cc/bluemonday) HTML sanitizer\n\n## Install\n\n`npm install bluemonday`\n\n## Usage\n\nWorks exactly like the golang version of bluemonday\n\n```\nvar p = bluemonday.UGCPolicy()\nvar html = p.Sanitize(\n    `\u003ca onblur=\"alert(secret)\" href=\"http://www.google.com\"\u003eGoogle\u003c/a\u003e`,\n)\n// Output:\n// \u003ca href=\"http://www.google.com\" rel=\"nofollow\"\u003eGoogle\u003c/a\u003e\n```\n\n## Credit\n\nAll credit goes to [bluemonday](https://github.com/microcosm-cc/bluemonday) and the [GopherJS](https://github.com/gopherjs/gopherjs) project for making this possible; I simply packaged it for npm.\n\n----\n\n# Turning GoLang code into an npm module with GopherJS\n\nRather than write up a seperate blog post about this, I thought I'd just stick it into the repo to keep things simple.\n\nSee the code [here](https://github.com/mdp/bluemonday-js)\n\n### Intro\n\nGopherJs compiles go to JavaScript and it works incredibly well for most projects. That being said, I didn't find any guides on how best to integrate that process into a modular JavaScript codebase. I built this project mostly as a demo of how to take go code and turn it into an npm module that can be used in Node or a modern browser.\n\nMy goals for this demonstration project are as follows:\n\n- Write go code that can be used from JavaScript\n- Package the code in an npm module\n- Write unit tests for the generated javascript code\n- Build and test the package using Travis\n- Deploy the package to npmjs.com's public repo\n\n### Structure\n\n```\n-- bluemonday-js\n  \\- index.js \t\t\u003c-- Our compiled go code\n  \\- test \t\t\t\u003c-- Unit tests for the javascript code\n  \\- go \t\t\t\u003c-- Directory containing all our go code\n  \\- package.json \t\u003c-- NPM's package.json\n  \\- Makefile \t\t\u003c-- the Makefile for Travis to build and test our repo\n  \\- .gitignore \t\u003c-- We ignore the index.js to prevent checking in generated code\n  \\- .npmigonre \t\u003c-- No need to have NPM include the go code in the package\n  \\- .travis.yml \t\u003c-- Travis setup\n```\n\n### The GoLang code\n\nIn this project I'm simply wrapping the [bluemonday](https://github.com/microcosm-cc/bluemonday) HTML sanitizer library with the necessary code to expose it in JavaScript.\n\nSince this will become a CommonJS module, we need to 'export' the functions we want to expose, just like in JS land. GopherJS provides a way to get global objects, so it's just a matter of grabbing `exports` and setting the properties like so:\n\n```go\nfunc myGoFunc() string {\n  return \"I'm written in go\"\n}\nfunc main() {\n\tjs.Module.Get(\"exports\").Set(\"myGoFunc\", myGoFunc)\n}\n```\n\nNow on the JS side, when we require the generated JS code, we'll be able to call `myGoFunc` just like you would expect.\n\nThe next step is wrapping and returning a more complex type rather than just a `string`. In my case I want to expose bluemonday's instance of the `Policy` struct to Javascript\n\n```go\nfunc UGCPolicy(name string) *js.Object {\n  return js.MakeWrapper(bluemonday.UGCPolicy())\n}\n\nfunc main() {\n  js.Module.Get(\"exports\").Set(\"UGCPolicy\", UGCPolicy)\n}\n```\n\nHere bluemonday will return the `*Policy` object when we call UGCPolicy, and we will then wrap it using Gopher's `MakeWrapper` function.\n\nCalling it from Node/JavaScript is then simply:\n\n```js\nlet bluemonday = require('bluemonday')\nlet p = bluemonday.UGCPolicy()\nlet html = p.Sanitize('\"\u003ca onblur=\"alert(secret)\" href=\"http://google.com\"\u003eDangerous Input\u003c/a\u003e\"')\n```\n\n### Build and Test process\n\nThe build process is simple with GopherJS:\n\n`gopherjs build go/main.go -o index.js`\n\nWe build the main.go file and output it to index.js.\n\nNext, we can run our tests directly against the index.js that we just outputted. In my case I use mocha, so the tests look something like:\n\n```js\nlet assert = require('chai').assert\nlet bluemonday = require('../index')\n\nlet input = '\u003ca onblur=\"alert(secret)\" href=\"http://www.google.com\"\u003eGoogle\u003c/a\u003e\u003cp\u003eYo\u003c/p\u003e'\nlet sanitized = '\u003ca href=\"http://www.google.com\" rel=\"nofollow\"\u003eGoogle\u003c/a\u003e\u003cp\u003eYo\u003c/p\u003e'\n\ndescribe('Basic markdown', function() {\n  it('should work with UGCPolicy', function(done) {\n    let p = bluemonday.UGCPolicy()\n    let html = p.Sanitize(input)\n    assert.equal(html, sanitized)\n    done()\n  })\n})\n```\n\n#### Travis CI\n\nThis ones a little less obvious. We have a bit of a Frankenstein project here; we want to test the JS code, but we also want Travis to build the JS code from our go code first. We could include the compiled JS in the repo, but I'd like to avoid that.\n\nTherefore, the requirements are a test image capable of running go \u003e= 1.5 and Node.js. In my experimenting with Travis we can easily get this by asking for a 'go' container with version 1.5 and then manually installing Node.js via `nvm`.\n\nMy .travis.yml looks like this.\n\n```yaml\nlanguage: go\n\ngo:\n  - 1.5\n\nbefore_script:\n  - nvm install 5.0; npm install\n\ninstall:\n  - go get github.com/microcosm-cc/bluemonday\n  - go get github.com/gopherjs/gopherjs\n```\n\nThis should all be fairly obvious. The next step is running the tests. In a 'go' project Travis will run `go test` unless there is a Makefile present, in which case it will simple run `make`. We will therefore need a Makefile to kick off the mocha tests. It's pretty simple:\n\n```make\nall: test\n\ntest:\n\tnpm run build; npm test\n\n.PHONY: all test\n\n```\n\nI'm calling the npm cli to run two scripts as part of the test. I have those scripts inside my package.json file as follows:\n\n1. `\"build\": \"gopherjs build go/main.go -m -o index.js\"`\n1. `\"test\": \"mocha --compilers js:babel-core/register test/*_test.js\"`\n\n### Deploying to npm\n\nThere are plenty of articles on how to do this (Basically `npm publish`), so I'll just point out a one caveat.\n\nI use `.gitignore` to prevent our compiled JS code from ending up in source control. npm will therefore also ignore the compiled code when it packages up the module, so we will need to use a `.npmignore` file to negate this. In the `.npmignore` I'm also excluding our go source from getting packaged.\n\n```\n/go\n```\n\nNow we have the git repo ignoring our compiled JS, and the npm package including it but excluding the go source code from the distributed module.\n\n### Conclusion\n\nThis all works surprisingly well. The module we end up with can be consumed by anyone via NPM and the process is farily easy once it's setup. I hope this helps anyone looking to do something similar. Feel free to file a pull request if you have anything to add.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdp%2Fbluemonday-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdp%2Fbluemonday-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdp%2Fbluemonday-js/lists"}