Skip to content

Latest commit

 

History

History
389 lines (289 loc) · 15.5 KB

CONTRIBUTING.md

File metadata and controls

389 lines (289 loc) · 15.5 KB

Contributing

Whether you have direct push access to the repo or are planning to submit a pull request for some content changes, the issues list is a good place to start. If there is an issue that interests you, comment on the issue and sign up for it. If you want to add some new content or modify existing content that doesn't have an associated issue, please file an issue so we can keep track and go for it.

Be Ready To Build the Site

If you want to test your changes locally, the make sure you have followed the installation instructions for locally building the site.

Two Main Sections

There are three main content sections for this repo, including two guides and one API reference:

  • hhvm is for user content on how to install, configure and use the HHVM runtime.
  • hack is for user content the features and tools associated with the Hack language.
  • There is a separate subsection for hack that is the API reference. This API reference is generated directly from the HHVM source code. But we also have the ability to provide examples for each API.

General Guidelines

While we aren't 100% rigid on how we want contributions to come to us (we want to make contributing as easy as possible), there are some guidelines that we ask you follow when making contributions.

  • Write great content :)
  • For the guides, we try to remain semi-informal. Please use the second person (e.g., "you") to give the reader a feel they are being targeted directly.
  • Focus on the user of Hack and HHVM; particularly, don't necessarily assume knowledge of a particular topic.
  • Get your content reviewed by peers for accuracy and feedback. This only helps make better documentation for everyone.
  • Code examples are awesome.
  • Edit the SCSS source, instead of the generated build/main.css file.

Document Structure

  • New topics or subtopics should follow the ##-topic/subtopic naming convention. e.g., 22-async or 07-guidelines.md. This is to allow documentation generation tooling an easier way to generate things like a table of contents. If necessary, you can rename/reorder topics or subtopics if it improves the logical flow.
  • With respect to the topic numbering and Hack:
    • The 00- topics are the more getting started topics.
    • The 20- topics are the key feature topics.
    • The 40- topics are additional topics.
    • There is one specialty numbering of 60- for unsupported features.

Code Examples

  • Code must, obviously, be written in Hack, unless you are specifically writing some sort of comparison, anti-pattern, etc.
  • Hack code must typecheck with hh_client correctly.

Code for Guides

Code samples in all .md files (guides as well as api-examples) should follow this format:

```example.hack
echo "Hello world!\n";
```

The build script, when processing this .md file, will extract the example code and execute it via HHVM, then include both the example code and the output in the rendered HTML.

The file name must be specified, so that the build script knows what name to use for the extracted file. In almost all cases, the file extension should be .hack, unless the example is specifically about a feature that requires a different extension (e.g. partial mode).

Boilerplate

The build script automatically adds common boilerplate code to each extracted example, e.g. the above echo statement would be automatically wrapped in an <<__EntryPoint>> function.

Example Output

Ideally, each code example should produce some output when HHVM runs it. For example, a sample class definition would ideally be followed by an <<__EntryPoint>> function that demonstrates its usage.

However, if this would distract too much from the purpose of the guide, it's acceptable for some examples to not produce any output (e.g. an example class definition without usage). The extracted example file will still be typechecked and run, so at least we will still catch any typechecker and/or runtime errors.

In rare cases, when it's impossible to make a code sample into valid Hack code without making it too distracting, you can include a "fake" example code block that will not be typechecked and executed. To do this, simply omit the file name:

```
// Example code block without a file name will not be typechecked/executed.
// Avoid these if at all possible.
```

Namespaces

By default, the build script will put each extracted example into a unique namespace, so each example must be standalone and cannot depend on others (e.g. use a class declared in another example code block).

To override this, you can give multiple examples a shared prefix in their filename followed by a period (.):

Here's an example class:
```example_class.definition.hack
class C {}
```
And its usage:
```example_class.usage.hack
$c = new C();
```

The build script will ignore everything after the first period in the filename when generating the namespace name.

Note that this only works for examples in a single guide (single .md file); there is currently no way to put examples from separate guides into a shared namespace.

Autoloading

HHVM requires an "autoloader" to be explicitly initialized whenever any Hack file references definitions from another Hack file.

The build script will insert the necessary initialization code automatically into any <<__EntryPoint>> function, so it is OK to rely on definitions from other examples inside any <<__EntryPoint>> function or functions called by it, but not elsewhere.

For example, HHVM can never successfully run a file containing e.g. a class definition that references a parent class or other definition from another file (this is not a limitation specific to the docs site).

```example_hierarchy.parent.hack
abstract class Parent {}
```

```example_hierarchy.child.hack
// This file will NEVER successfully run in HHVM.
final class Child extends Parent {}
```

In practice, this is fine because running a file containing a class definition is generally not needed. However, it does mean that trying to add an <<__EntryPoint>> function to example_hierarchy.child.hack won't work, because HHVM will fail with an "Undefined class Parent" error before it even reaches it.

```example_hierarchy.child.hack
// This file will NEVER successfully run in HHVM.
final class Child extends Parent {}

<<__EntryPoint>>
function main(): void {
  // This EntryPoint function is useless because HHVM will fail above.
}
```

The workaround is to put any code that depends on definitions from more than one other example into a separate code block.

```example_hierarchy.usage.hack
$c = new Child();
```

This can also be more convenient because we can rely on the automatic boilerplate addition by the build script, instead of manually writing the <<__EntryPoint>> function header.

Examples with Hack Errors

Examples that are expected to fail typechecking should use the .type-errors extension:

```error_example.hack.type-errors
function missing_return_type() {}
```

The build script will run the Hack typechecker and include its output in the rendered HTML (instead of HHVM runtime output).

Supporting Files

An example code block may specify additional files to be extracted alongside the example code using the following format:

```nondeterministic_example.hack
echo "Your lucky number is: ".\mt_rand(0, 99);
```.example.hhvm.out
Your lucky number is: 42
```.expectf
Your lucky number is: %d
```

Supported extensions are inherited from the HHVM test runner:

  • .hhconfig if the example requires specific typechecker flags (e.g. demonstrating a feature that is not yet enabled by default)
  • .ini for runtime flags
  • .hhvm.expect if you want to manually specify the expected output, instead of the build script doing it automatically
  • .hhvm.expectf to specify the expected output using printf-style syntax, like in the example above
  • .expectregex to specify the expected output using a regular expression
  • .example.hhvm.out should contain one possible output (this will be included in the rendered HTML instead of the expectf/expectregex file; it is not needed for regular expect files)
  • .typechecker.expect, .typechecker.expectf, .typechecker.expectregex, .example.typechecker.out are the same but for typechecker (Hack) output instead of runtime (HHVM) output; they should only be included if the example code is expected to fail typechecking and you don't want the build script to generate them automatically
  • .skipif should contain valid Hack code that will print "skip" if the example should not be run (e.g. a MySQL example that should not run if there isn't a MySQL server running), otherwise print nothing

Code For APIs

Adding a .md file to the correct subdirectory in api-examples will cause the build script to add its content to the respective API page.

The .md file may contain any combination of explanatory text and any number of code examples following the same rules as above.

Generated Markdown

This should be rare, but if you need to generate markdown on the fly (e.g., we do this for generating the HHVM Supported PHP INI Settings at http://docs.hhvm.com/hhvm/configuration/INI-settings#supported-php-ini-settings.

  • Create a BuildStep (PHP INI setting table example) for any data you might need to generate the markdown.
  • Create a BuildStep (PHP INI setting table example)for manually creating the markdown to be rendered.
  • Create a symlink (example) in the directory of the guide where the generated markdown will be rendered into. The symlink should point to GUIDES_GENERATED_MARKDOWN directory.
  • Update the actual guide that will have the generated markdown in it using the similar example syntax @@, but instead pointing to the .md file that will be inserted in the guide (as opposed to a .php) file. (example)
  • Add any test necessary (example).

Then our rendering process will add your generated markdown to your guide.

Linking Between Content

There are plenty of guide-to-guide links, guide-to-api links, etc. For internal links, we have a way to reference areas below. For external links, just use the absolute URL.

Images

Images should be referenced in the user guide or example headers as /public/images/imagename.png

API reference

To reference the API documentation, use the following format:

  • For classes: /hack/reference/class/<class-name>/
  • For interfaces: /hack/reference/interface/<interface-name>/
  • For traits: /hack/reference/trait/<trait-name>/
  • For methods: /hack/reference/[class | interface | trait]/<class | interface | trait>-name/<method-name>/
  • For functions: /hack/function/<function-name>/

The names of classes, functions, etc. may be namespaced qualified (e.g., HH.class_meth).

Guide Reference

To reference guide documentation, use the following format:

  • For HHVM: /hhvm/<topic>/<sub-topic>
  • For Hack: /hack/<topic>/<sub-topic>

The topics and subtopics do not have their directory named numerical prefixes associated with them.

e.g., /hack/lambdas/creation-story

Testing Changes

We have a test suite to ensure consistency across the changes we make to the guides, API references, and examples.

You can run it as follows:

$ vendor/bin/hacktest tests/

Running the Examples

Nearly all of the code examples you see in the guides and API documentation are actual Hack or PHP source files that are embedded at site build time into the content itself.

As opposed to embedded the code examples directly within the markdown itself, this provides the flexibility of actually having running examples within this repo.

You must have HHVM installed in order to run these examples since most of them are written in Hack (e.g., <?hh), and HHVM is the only runtime to currently support Hack.

You will find the examples in directories named with the pattern:

guides/[hhvm | hack]/##-topic/##-subtopic-examples

e.g.,

$ guides/hack/23-collections/06-constructing-examples

Standalone

You can run any example standalone. For example:

# Assuming you are in the user-documentation repo directory
% cd guides/hack/23-collections/10-examples-examples/
% hhvm lazy.php

And you will see output like:

object(HH\Vector)#4 (5) {
  [0]=>
  int(0)
  [1]=>
  int(2)
  [2]=>
  int(4)
  [3]=>
  int(6)
  [4]=>
  int(8)
}
Time non lazy: 0.10859489440918
object(HH\Vector)#10 (5) {
  [0]=>
  int(0)
  [1]=>
  int(2)
  [2]=>
  int(4)
  [3]=>
  int(6)
  [4]=>
  int(8)
}
Time non lazy: 0.0096559524536133

Using the HHVM Test Runner

Each example is structured to be run with the HHVM test runner. We use the test runner internally to ensure that any changes made to HHVM do not cause a regression. The examples in the documentation here can be used for that purpose as well.

You can run the HHVM test runner on the entire suite of examples, on one directory of examples or just one example itself.

Normally you will use our test suit described above to test any changes you make (because it tests our examples as well). However, sometimes it is actually faster and more explicit to test one example directly with the HHVM test runner.

# Assuming you are in the user-documentation repo root

# This runs every example in the test runner.
# Won't normally need to do this; just use our test suite instead.

# Test with the typechecker
$ api-sources/hhvm/hphp/test/run --hhserver-binary-path $(which hh_server) --typechecker guides/hack/05-statements/
# Test with the runtime
$ api-sources/hhvm/hphp/test/run --hhvm-binary-path $(which hhvm) guides/hack/05-statements/

Here is the output you should see when you run the test runner. Assume we are running the examples in the collections topic:

$ hhvm api-sources/hhvm/hphp/test/run guides/hack/23-collections/
Running 32 tests in 32 threads (0 in serial)

All tests passed.
              |    |    |
             )_)  )_)  )_)
            )___))___))___)\
           )____)____)_____)\
         _____|____|____|____\\__
---------\      SHIP IT      /---------
  ^^^^^ ^^^^^^^^^^^^^^^^^^^^^
    ^^^^      ^^^^     ^^^    ^^
         ^^^^      ^^^

Total time for all executed tests as run: 11.57s

You can use --verbose to see all the tests that are running.