如何为package.json添加注释以进行npm安装?


380

我有一个简单的package.json文件,我想添加一条注释。有没有办法做到这一点,或者有什么技巧可以做到这一点?

{
  "name": "My Project",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "express": "3.x",
    "mongoose": "3.x"
  },
  "devDependencies" :  {
    "should": "*"
    /* "mocha": "*" not needed as should be globally installed */
  }
}

上面的示例注释在npm中断时不起作用。我也尝试过//样式注释。



17
@YehudaKatz-我不认为这是重复的,因为该问题仅针对package.json文件,并且package.json在NodeJS邮件列表上有特定的答案。
马克·埃文斯

2
npm的核心开发人员之一拒绝考虑对的评论支持package.json。请对此问题发表评论-也许我们可以展示出有用的评论。
Dan Dascalescu 2015年

5
一个标签<sarcasm />。JSON5支持注释json5.org
Cristian E.

Answers:


450

最近在node.js邮件列表中对此进行了讨论。

根据创建npm的Isaac Schlueter的说法:

... npm绝不会将“ //”键用于任何目的,并且仅用于注释...如果要使用多行注释,则可以使用数组,也可以使用多个“ //”键。

使用常用工具(npm,yarn等)时,多个“ //”键将被删除。这样可以生存:

{ "//": [ 
  "first line", 
  "second line" ] } 

这将无法生存:

{ "//": "this is the first line of a comment", 
  "//": "this is the second line of the comment" } 

58
有没有办法记录“依赖项”部分中的每个条目是什么?当“ //”把戏的属性是“依赖项”时,它不起作用。
黑麦

8
请注意,如第一个示例中那样使用多个注释会{ "//": "first", "//": "second"}阻止您使用npm version和其他命令行工具,它们通常会重新解析整个JSON并丢弃正在处理的重复键。
jakub.g 2014年

60
必须意识到“ //”只能在对象的根部使用package.json。例如{ "dependencies": { "//": "comment?" }},无效但{ "//": "comment!", "dependencies":{}}有效。
david_p 2015年

52
甚至Douglas Crockford都可以将注释放入JSON配置文件中。至少可以说NPM的情况很愚蠢。
穆罕默德·

5
根据我的经验,"//"密钥及其价值最终会消失。有没有办法发表永久评论?
pruett

116

这是在JSON中添加注释的另一个技巧。以来:

{"a": 1, "a": 2}

相当于

{"a": 2}

您可以执行以下操作:

{
  "devDependencies": "'mocha' not needed as should be globally installed",
  "devDependencies" :  {
    "should": "*"
  }
}

12
这也适用于特定的程序包级别。例如。"express": "makes routing better so I don't want to gouge my eyes out", "express": "3.x"。因此,是的,就像ColinE所说的那样“糟糕”,也正如ColinE所说的那样“感谢”。
juanpaco 2014年

22
请注意,尽管这种破解阻止您package.json以编程方式更改,例如通过npm version 1.2.3更改版本-冗余条目将从生成的JSON中删除。
jakub.g 2014年

16
这是个坏建议,因为不能保证解释对象的顺序。例如,在某些情况下,你的例子可能用的是1,而不是2结束了
乔斯普拉格

6
@mpen的风险是不能保证解析JSON的代码将按顺序进行。
Jo Sprague

7
作为记录,RFC明确表示:“当对象中的名称不是唯一的时,接收到该对象的软件的行为是不可预测的。许多实现仅报告姓氏/值对。其他实现则报告错误或失败。解析对象,某些实现报告了所有名称/值对,包括重复项。”
Alan Tam

106

在花了一个小时处理复杂而棘手的解决方案之后,我发现了既简单又有效的解决方案,用于在中注释我的庞大依赖项部分package.json。像这样:

{
  "name": "package name",
  "version": "1.0",
  "description": "package description",
  "scripts": {
    "start": "npm install && node server.js"
  },
  "scriptsComments": {
    "start": "Runs development build on a local server configured by server.js"
  },
  "dependencies": {
    "ajv": "^5.2.2"
  },
  "dependenciesComments": {
    "ajv": "JSON-Schema Validator for validation of API data"
  }
}

如果以相同的方式进行排序,那么现在我很容易在git commit diffs或编辑器中跟踪这些成对的依赖关系/注释package.json

无需额外的工具,只需简单有效的JSON。

希望这对任何人有帮助。


1
这样更有意义并保持清洁。
Hitesh Sahu

4
感谢您提供的非hacky解决方案,该解决方案在技术上有效且在语义上有所帮助。
罗伊·廷克

5
对于脚本的评论,为什么不提供“帮助”脚本,例如 "scripts": { "postinstall": "echo postinstall stuff goes here", "help-postinstall": "echo helpful stuff goes here" }
高峰

1
@peak谢谢!我看到的唯一缺点是实际脚本将与注释混合在一起。
gkond

1
@gkond对此表示感谢。对我来说最有意义。
罗宾·温斯洛

20

许多有趣的想法。

我一直在做的是:

{
  ...
  "scripts": {
    "about": "echo 'Say something about this project'",
    "about:clean": "echo 'Say something about the clean script'",
    "clean": "do something",
    "about:build": "echo 'Say something about building it'",
    "build": "do something",
    "about:watch": "echo 'Say something about how watch works'",
    "watch": "do something",
  }
  ...
}

这样,我既可以阅读脚本本身中的“伪注释”,也可以运行如下所示的内容,以在终端中查看某种帮助:

npm run about
npm run about:watch

我的2cents在此讨论:)


聪明,我喜欢
KorreyD

14

NPS(节点程序包脚本)为我解决了这个问题。使您可以将NPM脚本放入一个单独的JS文件中,您可以在其中添加注释丰富的注释以及所需的任何其他JS逻辑。 https://www.npmjs.com/package/nps

package-scripts.js我的一个项目的样本

module.exports = {
  scripts: {
    // makes sure e2e webdrivers are up to date
    postinstall: 'nps webdriver-update',

    // run the webpack dev server and open it in browser on port 7000
    server: 'webpack-dev-server --inline --progress --port 7000 --open',

    // start webpack dev server with full reload on each change
    default: 'nps server',

    // start webpack dev server with hot module replacement
    hmr: 'nps server -- --hot',

    // generates icon font via a gulp task
    iconFont: 'gulp default --gulpfile src/deps/build-scripts/gulp-icon-font.js',

    // No longer used
    // copyFonts: 'copyfiles -f src/app/glb/font/webfonts/**/* dist/1-0-0/font'
  }
}

我只是进行了本地安装npm install nps -save-dev,并将其放入package.json脚本中。

"scripts": {
    "start": "nps",
    "test": "nps test"
}

13

您总是可以滥用重复密钥被覆盖的事实。这就是我刚刚写的:

"dependencies": {
  "grunt": "...",
  "grunt-cli": "...",

  "api-easy": "# Here is the pull request: https://github.com/...",
  "api-easy": "git://..."

  "grunt-vows": "...",
  "vows": "..."
}

但是,尚不清楚JSON是否允许重复键(请参阅 JSON语法是否允许对象中重复键?。它似乎可以与npm配合使用,所以我冒险。

推荐的技巧是使用"//"密钥(来自nodejs邮件列表)。当我对其进行测试时,它不适用于“依赖项”部分。另外,帖子中的示例使用多个"//"密钥,这意味着npm不会拒绝具有重复密钥的JSON文件。换句话说,上面的技巧应该总是可以的。

更新:重复密钥破解的一个令人讨厌的缺点是,npm install --save静默消除了所有重复项。不幸的是,很容易忽略它,而您的好意也就消失了。

"//"黑客仍是最安全的,因为它似乎。但是,多行注释也将被删除npm install --save


1
"//"hack在devDependencies内部不起作用。NPM尝试解析UNC路径。
德米特里S.16年

感谢您的更新句子,但再次不能评论mocha属性。只是它可以添加其中一项以上,最后将由npm使用。
vusan

我不愿意承认这一点-但是我比“ //”更喜欢
roocell

9

我有一个有趣的技巧。

例如,适当地创建npm包名称作为注释分隔符dependenciesdevDependencies在package.json中阻止x----x----x

{
    "name": "app-name",
    "dependencies": {
        "x----x----x": "this is the first line of a comment",
        "babel-cli": "6.x.x",
        "babel-core": "6.x.x",
        "x----x----x": "this is the second line of a comment",
        "knex": "^0.11.1",
        "mocha": "1.20.1",
        "x----x----x": "*"
    }
}

注意:必须添加带有有效版本的最后一个注释分隔线,如*方框中所示。


6
是的,它实际上可用:npmjs.com/package/x
x----

9
对此答案感到很兴奋,但是在我运行npm install(使用npm 5)后,我的重复密钥被自动删除了:(
Eric Majerus

@EricMajerus oops〜,npm5也让我伤心了:(
廖三开

8

受此线程的启发,这是我们正在使用的内容

{
  "//dependencies": {
    "crypto-exchange": "Unified exchange API"
  },
  "dependencies": {
    "crypto-exchange": "^2.3.3"
  },
  "//devDependencies": {
    "chai": "Assertions",
    "mocha": "Unit testing framwork",
    "sinon": "Spies, Stubs, Mocks",
    "supertest": "Test requests"
  },
  "devDependencies": {
    "chai": "^4.1.2",
    "mocha": "^4.0.1",
    "sinon": "^4.1.3",
    "supertest": "^3.0.0"
  }
}

7

到目前为止,这里的大多数“黑客”都建议滥用JSON。但是,为什么不滥用底层脚本语言呢?

编辑最初的响应是将说明放在右边以# add comments here用于包装;但是,这在Windows上不起作用,因为标志(例如npm运行myframework---myframework-flags)将被忽略。我更改了响应使其可以在所有平台上运行,并出于可读性目的添加了一些缩进。

{
 "scripts": {
    "help": "       echo 'Display help information (this screen)';          npm run",
    "myframework": "echo 'Run myframework binary';                          myframework",
    "develop": "    echo 'Run in development mode (with terminal output)';  npm run myframework"
    "start": "      echo 'Start myFramework as a daemon';                   myframework start",
    "stop":  "      echo 'Stop the myFramework daemon';                     myframework stop"
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

这将:

  1. 不违反JSON规范(或至少不违反hack,并且您的IDE不会警告您做奇怪,危险的事情)
  2. 跨平台工作(在macOS和Windows上进行了测试,假设它在Linux上可以正常工作)
  3. 不会妨碍运行 npm run myframework -- --help
  4. 运行时将输出有意义的信息npm run(这是运行以获取有关可用脚本的信息的实际命令)
  5. 提供更明确的帮助命令(以防某些开发人员不知道npm run提供此类输出)
  6. 运行命令本身时将同时显示命令及其说明
  7. 刚打开package.json(使用less或您最喜欢的IDE)时有点可读

Argh,实际上在Windows上它只会忽略标志,因此3.不是正确的:/
Marc Trudel

使它与Windows cmd兼容:&&而不是;使第一个命令变为:"help": "echo 'Display help information (this screen)' && npm run",
phil_lgr

1
是的,那就是我最终要做的。接得好!
马克·特鲁德尔

3
它仅在本scripts节中有效。package.json还有很多其他的事情。
罗尔夫

正确。再说一遍,您还有什么需要在那里记录的感觉?
马克·特鲁德尔

6

这是我对package.json/中的评论的看法bower.json

我有package.json.js一个包含导出实际脚本的脚本package.json。运行脚本会覆盖旧的脚本,package.json并告诉我它进行了哪些更改,非常适合帮助您跟踪npm所做的自动更改。这样,我什至可以以编程方式定义我要使用的软件包。

最新的grunt任务在这里:https : //gist.github.com/MarZab/72fa6b85bc9e71de5991


我认为这在许多方面都是“正确”的答案(使用差异补丁去除注释以解决脱网后更改的任务)-但是,我感觉到咕gr声任务的增加并不是某些人的责任之后,对于小型项目,可能最好保留一个外部文件以进行注释并使用NPM scrpts(完全避免构建任务)。对于大型项目,您可能正在使用某种形式的任务运行程序,因此这种方法似乎很可靠。在这两者之间,我认为也许使“ //”建议适应口味(避免一个人的特定痛点)可能是最好的。
Enull 2016年

1
我喜欢这个主意,但是正如有人要问的那样,您通过npm install --saveor 修改原始package.json的情况又--save-dev如何呢?
等时的

是的,我一直想念那些评论;没有好的解决方案,我过去经常查看git
diffs,

1

我最终得到scripts这样的结果:

  "scripts": {
    "//-1a": "---------------------------------------------------------------",
    "//-1b": "---------------------- from node_modules ----------------------",
    "//-1c": "---------------------------------------------------------------",
    "ng": "ng",
    "prettier": "prettier",
    "tslint": "tslint",
    "//-2a": "---------------------------------------------------------------",
    "//-2b": "--------------------------- backend ---------------------------",
    "//-2c": "---------------------------------------------------------------",
    "back:start": "node backend/index.js",
    "back:start:watch": "nodemon",
    "back:build:prod": "tsc -p backend/tsconfig.json",
    "back:serve:prod": "NODE_ENV=production node backend/dist/main.js",
    "back:lint:check": "tslint -c ./backend/tslint.json './backend/src/**/*.ts'",
    "back:lint:fix": "yarn run back:lint:check --fix",
    "back:check": "yarn run back:lint:check && yarn run back:prettier:check",
    "back:check:fix": "yarn run back:lint:fix; yarn run back:prettier:fix",
    "back:prettier:base-files": "yarn run prettier './backend/**/*.ts'",
    "back:prettier:fix": "yarn run back:prettier:base-files --write",
    "back:prettier:check": "yarn run back:prettier:base-files -l",
    "back:test": "ts-node --project backend/tsconfig.json node_modules/jasmine/bin/jasmine ./backend/**/*spec.ts",
    "back:test:watch": "watch 'yarn run back:test' backend",
    "back:test:coverage": "echo TODO",
    "//-3a": "---------------------------------------------------------------",
    "//-3b": "-------------------------- frontend ---------------------------",
    "//-3c": "---------------------------------------------------------------",
    "front:start": "yarn run ng serve",
    "front:test": "yarn run ng test",
    "front:test:ci": "yarn run front:test --single-run --progress=false",
    "front:e2e": "yarn run ng e2e",
    "front:e2e:ci": "yarn run ng e2e --prod --progress=false",
    "front:build:prod": "yarn run ng build --prod --e=prod --no-sourcemap --build-optimizer",
    "front:lint:check": "yarn run ng lint --type-check",
    "front:lint:fix": "yarn run front:lint:check --fix",
    "front:check": "yarn run front:lint:check && yarn run front:prettier:check",
    "front:check:fix": "yarn run front:lint:fix; yarn run front:prettier:fix",
    "front:prettier:base-files": "yarn run prettier \"./frontend/{e2e,src}/**/*.{scss,ts}\"",
    "front:prettier:fix": "yarn run front:prettier:base-files --write",
    "front:prettier:check": "yarn run front:prettier:base-files -l",
    "front:postbuild": "gulp compress",
    "//-4a": "---------------------------------------------------------------",
    "//-4b": "--------------------------- cypress ---------------------------",
    "//-4c": "---------------------------------------------------------------",
    "cy:open": "cypress open",
    "cy:headless": "cypress run",
    "cy:prettier:base-files": "yarn run prettier \"./cypress/**/*.{js,ts}\"",
    "cy:prettier:fix": "yarn run front:prettier:base-files --write",
    "cy:prettier:check": "yarn run front:prettier:base-files -l",
    "//-5a": "---------------------------------------------------------------",
    "//-5b": "--------------------------- common ----------------------------",
    "//-5c": "---------------------------------------------------------------",
    "all:check": "yarn run back:check && yarn run front:check && yarn run cy:prettier:check",
    "all:check:fix": "yarn run back:check:fix && yarn run front:check:fix && yarn run cy:prettier:fix",
    "//-6a": "---------------------------------------------------------------",
    "//-6b": "--------------------------- hooks -----------------------------",
    "//-6c": "---------------------------------------------------------------",
    "precommit": "lint-staged",
    "prepush": "yarn run back:lint:check && yarn run front:lint:check"
  },

我的目的不是要澄清一行,而是要在我的脚本之间为后端,前端等所有内容使用某种分隔符。

我不是1a,1b,1c,2a等的忠实拥护者,但是按键是不同的,我完全没有问题。


1

就像这个答案所解释的那样,//密钥是保留的,因此可以按常规用于注释。//注释的问题在于,它不能在字符串中用作版本约束,dependencies并且不能devDependencies作为常规依赖项使用:

"dependencies": {
  "//": "comment"
}

触发错误,

npm ERR!代码EINVALIDPACKAGENAME

npm ERR!无效的包名称“ //”:名称只能包含URL友好字符

尽管具有非字符串值的键被认为是无效的依赖关系,并被有效地忽略:

"dependencies": {
  "//": ["comment"]
}

依赖项本身可以用相同的方式注释掉:

"dependencies": {
  "foo": ["*", "is not needed now"],
}

由于依赖项在NPM修改package.json时进行排序,因此在其引用的依赖项之上放置注释是不切实际的:

"dependencies": {
  "bar": "*",
  "//": ["should be removed in 1.x release"]
  "foo": "*",
}

如果注释键指向特定行,则应相应地命名注释键,因此不会被移动:

"dependencies": {
  "bar": "*",
  "foo": "*",
  "foo //": ["should be removed in 1.x release"]
}

可将适用于特定依赖项的注释作为semver的一部分添加:

"dependencies": {
  "bar": "*",
  "foo": "* || should be removed in 1.x release"
}

请注意,如果之前的第一部分OR不匹配,则可以解析注释1.x

这些解决方法与所有当前NPM版本(6及更低版本)兼容。


1

由于大多数开发人员都熟悉基于标签/注释的文档,因此我开始使用的约定是相似的。这是一种味道:

{
  "@comment dependencies": [
    "These are the comments for the `dependencies` section.",
    "The name of the section being commented is included in the key after the `@comment` 'annotation'/'tag' to ensure the keys are unique.",
    "That is, using just \"@comment\" would not be sufficient to keep keys unique if you need to add another comment at the same level.",
    "Because JSON doesn't allow a multi-line string or understand a line continuation operator/character, just use an array for each line of the comment.",
    "Since this is embedded in JSON, the keys should be unique.",
    "Otherwise JSON validators, such as ones built into IDE's, will complain.",
    "Or some tools, such as running `npm install something --save`, will rewrite the `package.json` file but with duplicate keys removed.",
    "",
    "@package react - Using an `@package` 'annotation` could be how you add comments specific to particular packages."
  ],
  "dependencies": {
    ...
  },
  "scripts": {
    "@comment build": "This comment is about the build script.",
    "build": "...",

    "@comment start": [
      "This comment is about the `start` script.",
      "It is wrapped in an array to allow line formatting.",
      "When using npm, as opposed to yarn, to run the script, be sure to add ` -- ` before adding the options.",
      "",
      "@option {number} --port - The port the server should listen on."
    ],
    "start": "...",

    "@comment test": "This comment is about the test script.",
    "test": "..."
  }
}

注意:对于dependenciesdevDependencies等的部分中,注释的注释不能直接上述结构物体内部的单个包装体的依赖关系,因为加入npm期待的关键是NPM包的名称。因此,产生这种情况的原因@comment dependencies

注意:在某些情况下,例如在脚本对象中,某些编辑器/ IDE可能会抱怨该数组。在脚本上下文中,VS Code需要一个字符串作为值-而不是数组。

我喜欢将注释添加到JSON的注释/标签样式方式,因为该@符号在常规声明中脱颖而出。


1

总结所有这些答案:

  1. 添加一个单独的顶级字段//,其中包含注释字符串。这行得通,但是很烂,因为您不能在评论中添加评论。

  2. 添加多个以开头的顶级字段,例如包含注释字符串的字段。这样比较好,但仍然只允许您进行顶级评论。您不能评论单个依赖项。 ////dependencies

  3. echo命令添加到scripts。这行得通,但是很烂,因为您只能在中使用它scripts

这些解决方案也不是很可读。它们会增加大量的视觉噪音,并且IDE不会将语法突出显示为注释。

我认为唯一合理的解决方案是package.json从另一个文件生成。最简单的方法是将JSON编写为Javascript,然后使用Node将其写入package.json。将此文件另存为package.json.mjschmod +x然后就可以运行它来生成您的文件了package.json

#!/usr/bin/env node

import { writeFileSync } from "fs";

const config = {
  // TODO: Think of better name.
  name: "foo",
  dependencies: {
    // Bar 2.0 does not work due to bug 12345.
    bar: "^1.2.0",
  },
  // Look at these beautify comments. Perfectly syntax highlighted, you
  // can put them anywhere and there no risk of some tool removing them.
};

writeFileSync("package.json", JSON.stringify({
    "//": "This file is \x40generated from package.json.mjs; do not edit.",
    ...config
  }, null, 2));

它使用//密钥来警告人们不要对其进行编辑。\x40generated是故意的 它变成@generatedpackage.json和手段一些代码审查系统将默认折叠该文件。

这是构建系统中的一个额外步骤,但它击败了此处的所有其他黑客。


0

在运行package.json工具(npm,yarn等)时,删除了重复的注释键后,我开始使用哈希版本,以便更好地读取多行和键,例如

"//": {
  "alpaca": "we use the bootstrap version",
  "eonasdan-bootstrap-datetimepicker": "instead of bootstrap-datetimepicker",
  "moment-with-locales": "is part of moment"
},

根据我的IDE作为根键,这是“有效的”,但其中dependencies却抱怨期望字符串值。


是的B / C,你不能真正而是//无处不在的关键,它不是真正征求意见的良好替代品,特别是当评论可以有很好的语法与编辑等突出
亚历山大·米尔斯

0

另一个hack。我创建了一个脚本来读取package.json作为车把模板的上下文。

下面的代码,以防有人觉得这种方法有用:

const templateData = require('../package.json');
const Handlebars = require('handlebars');
const fs = require('fs-extra');
const outputPath = __dirname + '/../package-json-comments.md';
const srcTemplatePath = __dirname + '/package-json-comments/package-json-comments.hbs';

Handlebars.registerHelper('objlist', function() {
  // first arg is object, list is a set of keys for that obj
  const obj = arguments[0];
  const list = Array.prototype.slice.call(arguments, 1).slice(0,-1);

  const mdList = list.map(function(k) {
    return '* ' + k + ': ' + obj[k];
  });

  return new Handlebars.SafeString(mdList.join("\n"));
});

fs.readFile(srcTemplatePath, 'utf8', function(err, srcTemplate){
  if (err) throw err;
  const template = Handlebars.compile(srcTemplate);
  const content = template(templateData);

  fs.writeFile(outputPath, content, function(err) {
    if (err) throw err;
  });
});

车把模板文件 package-json-comments.hbs

### Dependency Comments
For package: {{ name }}: {{version}}

#### Current Core Packages
should be safe to update
{{{objlist dependencies
           "@material-ui/core"
           "@material-ui/icons"
           "@material-ui/styles"
}}}

#### Lagging Core Packages
breaks current code if updated
{{{objlist dependencies
           "amazon-cognito-identity-js"
}}}

#### Major version change
Not tested yet
{{{objlist dependencies
           "react-dev-utils"
           "react-redux"
           "react-router"
           "redux-localstorage-simple"

}}}

0

对于npm package.json,找到了两种方法(阅读此对话后):

  "devDependencies": {
    "del-comment": [
      "some-text"
    ],
    "del": "^5.1.0 ! inner comment",
    "envify-comment": [
      "some-text"
    ],
    "envify": "4.1.0 ! inner comment"
  }

但是,使用“ --save”或“ --save-dev”更新或重新安装软件包时,请像“ ^ 4.1.0”这样注释!相应位置的“注释”将被删除。所有这一切都会破坏npm审核。


这不会尝试安装名为del-comment和的软件包envify-comment吗?
贝尼·切尔尼亚夫斯基-帕斯金

-1

我对JSON中没有注释的无奈感到沮丧。我创建了新节点,以它们所引用的节点命名,但以下划线作为前缀。这是不完美的,但是可以起作用。

{
  "name": "myapp",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^16.3.2",
    "react-dom": "^16.3.2",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "__start": [
        "a note about how the start script works"
    ],
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "__proxy": [
    "A note about how proxy works",
    "multilines are easy enough to add"
  ],
  "proxy": "http://server.whatever.com:8000"
}

使用start_comment会更好,因为那样会按字母顺序排序
Alexander Mills
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.