summaryrefslogtreecommitdiff
path: root/doc/development/gitlab_flavored_markdown/specification_guide/index.md
diff options
context:
space:
mode:
Diffstat (limited to 'doc/development/gitlab_flavored_markdown/specification_guide/index.md')
-rw-r--r--doc/development/gitlab_flavored_markdown/specification_guide/index.md717
1 files changed, 717 insertions, 0 deletions
diff --git a/doc/development/gitlab_flavored_markdown/specification_guide/index.md b/doc/development/gitlab_flavored_markdown/specification_guide/index.md
new file mode 100644
index 00000000000..021f7bafce9
--- /dev/null
+++ b/doc/development/gitlab_flavored_markdown/specification_guide/index.md
@@ -0,0 +1,717 @@
+---
+stage: Create
+group: Editor
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# GitLab Flavored Markdown (GLFM) Specification Guide **(FREE)**
+
+GitLab supports Markdown in various places. The Markdown dialect we use is called
+GitLab Flavored Markdown, or GLFM.
+
+The specification for the GLFM dialect is based on the
+[GitHub Flavored Markdown (GFM) specification](https://github.github.com/gfm/),
+which is in turn based on the [CommonMark specification](https://spec.commonmark.org/current/).
+The GLFM specification includes
+[several extensions](../../../user/markdown.md#differences-between-gitlab-flavored-markdown-and-standard-markdown)
+to the GFM specification.
+
+See the [section on acronyms](#acronyms-glfm-ghfm-gfm-commonmark) for a
+detailed explanation of the various acronyms used in this document.
+This guide is a developer-facing document that describes the various terms and
+definitions, goals, tools, and implementations related to the GLFM specification.
+It is intended to support and augment the [user-facing documentation](../../../user/markdown.md)
+for GitLab Flavored Markdown.
+
+NOTE:
+In this document, _GFM_ refers to _GitHub_ Flavored Markdown, not _GitLab_ Flavored Markdown.
+Refer to the [section on acronyms](#acronyms-glfm-ghfm-gfm-commonmark)
+for a detailed explanation of the various acronyms used in this document.
+
+NOTE:
+This guide and the implementation and files described in it are still a work in
+progress. As the work progresses, rewrites and consolidation
+between this guide and the [user-facing documentation](../../../user/markdown.md)
+for GitLab Flavored Markdown are likely.
+
+## Terms and definitions
+
+### Acronyms: GLFM, GHFM, GFM, CommonMark
+
+[_GitHub_ Flavored Markdown](https://github.github.com/gfm/) is widely referred
+to by the acronym GFM, and this document follows that convention as well.
+_GitLab_ Flavored Markdown is referred to as GLFM in this document,
+to distinguish it from GitHub Flavored Markdown.
+
+Unfortunately, this convention is not followed consistently in the rest
+of the documentation or GitLab codebase. In many places, the GFM
+acronym is used to refer to _GitLab_ Flavored Markdown. An
+[open issue](https://gitlab.com/gitlab-org/gitlab/-/issues/24592) exists to resolve
+this inconsistency.
+
+Some places in the code refer to both the GitLab and GitHub specifications
+simultaneous in the same areas of logic. In these situations,
+_GitHub_ Flavored Markdown may be referred to with variable or constant names like
+`ghfm_` to avoid confusion.
+
+The original CommonMark specification is referred to as _CommonMark_ (no acronym).
+
+### Various Markdown specifications
+
+The specification format we use is based on the approach used in CommonMark, where
+a `spec.txt` file serves as documentation, as well as being in a format that can
+serve as input to automated conformance tests. It is
+[explained in the CommonMark specification](https://spec.commonmark.org/0.30/#about-this-document):
+
+> This document attempts to specify Markdown syntax unambiguously. It contains many
+> examples with side-by-side Markdown and HTML. These are intended to double as conformance tests.
+
+The HTML-rendered versions of the specifications:
+
+- [GitLab Flavored Markdown (GLFM) specification](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output/spec.html), which extends the:
+- [GitHub Flavored Markdown (GFM) specification](https://github.github.com/gfm/), which extends the:
+- [CommonMark specification](https://spec.commonmark.org/0.30/)
+
+NOTE:
+The creation of the
+[GitLab Flavored Markdown (GLFM) specification](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output/spec.html)
+file is still pending.
+
+However, GLFM has more complex parsing, rendering, and testing requirements than
+GFM or CommonMark. Therefore,
+it does not have a static, hardcoded, manually updated `spec.txt`. Instead, the
+GLFM `spec.txt` is automatically generated based on other input files. This process
+is explained in detail in the [Implementation](#implementation) sections below.
+
+### Markdown examples
+
+Everywhere in the context of the specification and this guide, the term
+_examples_ is specifically used to refer to the Markdown + HTML pairs used
+to illustrate the canonical parsing (or rendering) behavior of various Markdown source
+strings in the standard
+[CommonMark specification format](https://spec.commonmark.org/0.30/#example-1).
+
+In this context, it should not be confused with other similar or related meanings of
+_example_, such as
+[RSpec examples](https://relishapp.com/rspec/rspec-core/docs/example-groups/basic-structure-describe-it).
+
+### Parsers and renderers
+
+To understand the various ways in which a specification is used, and how it related
+to a given Markdown dialect, it's important to understand the distinction between
+a _parser_ and a _renderer_:
+
+- A Markdown _parser_ accepts Markdown as input and produces a Markdown
+ Abstract Syntax Tree (AST) as output.
+- A Markdown _renderer_ accepts the AST produced by a parser, and produces HTML
+ (or a PDF, or any other relevant rendering format) as output.
+
+### Types of Markdown tests driven by the GLFM specification
+
+The two main types of automated testing are driven by the Markdown
+examples and data contained in the GLFM specification. We refer to them as:
+
+- Markdown conformance testing.
+- Markdown snapshot testing.
+
+Many other types of tests also occur in the GitLab
+codebase, and some of these tests are also related to the GLFM Markdown dialect.
+Therefore, to avoid confusion, we use these standard terms for the two types
+of specification-driven testing referred to in this documentation and elsewhere.
+
+#### Markdown conformance testing
+
+_Markdown conformance testing_ refers to the standard testing method used by
+all CommonMark Markdown dialects to verify that a specific implementation conforms
+to the CommonMark Markdown specification. It is enforced by running the standard
+CommonMark tool [`spec_tests.py`](https://github.com/github/cmark-gfm/blob/master/test/spec_tests.py)
+against a given `spec.txt` specification and the implementation.
+
+NOTE:
+`spec_tests.py` may eventually be re-implemented in Ruby, to not have a dependency on Python.
+
+#### Markdown snapshot testing
+
+_Markdown snapshot testing_ refers to the automated testing performed in
+the GitLab codebase, which is driven by snapshot fixture data derived from the
+GLFM specification. It consists of both backend RSpec tests and frontend Jest tests
+which use the fixture data. This fixture data is contained in YAML files. These files
+can be generated and updated based on the Markdown examples in the specification,
+and the existing GLFM parser and render implementations. They may also be
+manually updated as necessary to test-drive incomplete implementations.
+Regarding the terminology used here:
+
+1. The Markdown snapshot tests can be considered a form of the
+ [Golden Master Testing approach](https://www.google.com/search?q=golden+master+testing),
+ which is also referred to as Approval Testing or Characterization Testing.
+ 1. The term Golden Master originally comes from the recording industry, and
+ refers to the process of mastering, or making a final mix from which all
+ other copies are produced.
+ 1. For more information and background, you can read about
+ [Characterization Tests](https://en.wikipedia.org/wiki/Characterization_test) and
+ [Golden Masters](https://en.wikipedia.org/wiki/Gold_master_(disambiguation)).
+1. The usage of the term _snapshot_ does not refer to the approach of
+ [Jest snapshot testing](https://jestjs.io/docs/snapshot-testing), as used elsewhere
+ in the GitLab frontend testing suite. However, the Markdown snapshot testing does
+ follow the same philosophy and patterns as Jest snapshot testing:
+ 1. Snapshot fixture data is represented as files which are checked into source control.
+ 1. The files can be automatically generated and updated based on the implementation
+ of the code under tests.
+ 1. The files can also be manually updated when necessary, for example, to test-drive
+ changes to an incomplete or buggy implementation.
+1. The usage of the term _fixture_ does not refer to standard
+ [Rails database fixture files](https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html).
+ It instead refers to _test fixtures_ in the
+ [more generic definition](https://en.wikipedia.org/wiki/Test_fixture#Software),
+ as input data to support automated testing. However, fixture files still exist, so
+ they are colocated under the `spec/fixtures` directory with the rest of
+ the fixture data for the GitLab Rails application.
+
+## Parsing and Rendering
+
+The Markdown dialect used in the GitLab application has a dual requirement for rendering:
+
+1. Rendering to static read-only HTML format, to be displayed in various
+ places throughout the application.
+1. Rendering editable content in the
+ [Content Editor](https://about.gitlab.com/direction/create/editor/content_editor/),
+ a ["What You See Is What You Get" (WYSIWYG)](https://en.wikipedia.org/wiki/WYSIWYG)
+ editor. The Content Editor supports real-time instant switching between an editable
+ Markdown source and an editable WYSIWYG document.
+
+These requirements means that GitLab has two independent parser and renderer
+implementations:
+
+1. The backend parser / renderer supports parsing and rendering to _static_
+ read-only HTML. It is [implemented in Ruby](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/banzai).
+ It leverages the [`commonmarker`](https://github.com/gjtorikian/commonmarker) gem,
+ which is a Ruby wrapper for [`libcmark-gfm`](https://github.com/github/cmark),
+ GitHub's fork of the reference parser for CommonMark. `libcmark-gfm` is an extended
+ version of the C reference implementation of [CommonMark](http://commonmark.org/)
+1. The frontend parser / renderer supports parsing and _WYSIWYG_ rendering for
+ the Content Editor. It is implemented in JavaScript. Parsing is based on the
+ [Remark](https://github.com/remarkjs/remark) Markdown parser, which produces a
+ MDAST Abstract Syntax Tree (MDAST). Rendering is the process of turning
+ an MDAST into a [ProseMirror document](../../fe_guide/content_editor.md). Then,
+ ProseMirror is used to render a ProseMirror document to WYSIWYG HTML. In this
+ document, we refer to the process of turning Markdown into an MDAST as the
+ _frontend / JavaScript parser_, and the entire process of rendering Markdown
+ to WYSIWYG HTML in ProseMirror as the _Content Editor_. Several
+ requirements drive the need for an independent frontend parser / renderer
+ implementation, including:
+ 1. Lack of necessary support for accurate source mapping in the HTML renderer
+ implementation used on the backend.
+ 1. Latency and bandwidth concerns: eliminating the need for a round-trip to the backend
+ every time the user switches between the Markdown source and the WYSIWYG document.
+ 1. Different HTML and browser rendering requirements for WYSIWYG documents. For example,
+ displaying read-only elements such as diagrams and references in an editable form.
+
+### Multiple versions of rendered HTML
+
+Both of these GLFM renderer implementations (static and WYSIWYG) produce
+HTML which differs from the canonical HTML examples from the specification.
+For every Markdown example in the GLFM specification, three
+versions of HTML can potentially be rendered from the example:
+
+1. **Static HTML**: HTML produced by the backend (Ruby) renderer, which
+ contains extra styling and behavioral HTML. For example, **Create task** buttons
+ added for dynamically creating an issue from a task list item.
+ The GitLab [Markdown API](../../../api/markdown.md) generates HTML
+ for a given Markdown string using this method.
+1. **WYSIWYG HTML**: HTML produced by the frontend (JavaScript) Content Editor,
+ which includes parsing and rendering logic. Used to present an editable document
+ in the ProseMirror WYSIWYG editor.
+1. **Canonical HTML**: The clean, basic version of HTML rendered from Markdown.
+ 1. For the examples which come from the CommonMark specification and
+ GFM extensions specification,
+ the canonical HTML is the exact identical HTML found in the
+ GFM
+ `spec.txt` example blocks.
+ 1. For GLFM extensions to the <abbr title="GitHub Flavored Markdown">GFM</abbr> / CommonMark
+ specification, a `glfm_canonical_examples.txt`
+ [input specification file](#input-specification-files) contains the
+ Markdown examples and corresponding canonical HTML examples.
+
+As the rendered static and WYSIWYG HTML from the backend (Ruby) and frontend (JavaScript)
+renderers contain extra HTML, their rendered HTML can be converted to canonical HTML
+by a [canonicalization](#canonicalization-of-html) process.
+
+#### Canonicalization of HTML
+
+Neither the backend (Ruby) nor the frontend (JavaScript) rendered can directly render canonical HTML.
+Nor should they be able to, because:
+
+- It's not a direct requirement to support any GitLab application feature.
+- Adding this feature adds unnecessary requirements and complexity to the implementations.
+
+Instead, the rendered static or WYSIWYG HTML is converted to canonical HTML by a
+_canonicalization_ process. This process can strip all the extra styling and behavioral
+HTML from the static or WYSIWYG HTML, resulting in canonical HTML which exactly
+matches the Markdown + HTML examples in a standard `spec.txt` specification.
+
+Use the [`canonicalize-html.rb` script](#canonicalize-htmlrb-script) for this process.
+More explanation about this canonicalization process in the sections below.
+
+NOTE:
+Some of the static or WYSIWYG HTML examples may not be representable as canonical
+HTML. (For example, when they are represented as an image.) In these cases, the Markdown
+conformance test for the example can be skipped by setting `skip_update_example_snapshots: true`
+for the example in `glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml`.
+
+## Goals
+
+Given the constraints above, we have a few goals related to the GLFM
+specification and testing infrastructure:
+
+1. A canonical `spec.txt` exists, and represents the official specification for
+ GLFM, which meets these requirements:
+ 1. The spec is a strict superset of the GitHub Flavored Markdown
+ (GFM) specification, just as
+ <abbr title="GitHub Flavored Markdown">GFM</abbr> is a strict superset
+ [of the CommonMark specification](https://github.github.com/gfm/#what-is-github-flavored-markdown-).
+ Therefore, it contains the superset of all canonical Markdown + HTML examples
+ for CommonMark, GFM, and GLFM.
+ 1. It contains a prose introduction section which is specific to GitLab and GLFM.
+ 1. It contains all other non-introduction sections verbatim from the
+ GFM
+ `spec.txt`.
+ 1. It contains a new extra section for the GLFM GitLab-specific extensions,
+ with both prose and examples describing the extensions.
+ 1. It should be in the standard format which can processed by the standard
+ CommonMark tools [`spec_tests.py`](https://github.com/github/cmark-gfm/blob/master/test/spec_tests.py),
+ which is a [script used to run the Markdown conformance tests](https://github.github.com/gfm/#about-this-document)
+ against all examples contained in a `spec.txt`.
+1. The GLFM parsers and HTML renderers for
+ both the static backend (Ruby) and WYSIWYG frontend (JavaScript) implementations
+ support _consistent_ rendering of all canonical Markdown + HTML examples in the
+ GLFM `spec.txt` specification, as verified by `spec_tests.py`.
+
+ NOTE:
+ Consistent does not mean that both of these implementations render
+ to the identical HTML. They each have different implementation-specific additions
+ to the HTML they render, so therefore their rendered HTML is
+ ["canonicalized"](#canonicalization-of-html) to canonical HTML prior running
+ the Markdown conformance tests.
+1. For _both_ the static backend (Ruby) and WYSIWYG frontend (JavaScript) implementations,
+ a set of example snapshots exists in the form of YAML files, which
+ correspond to every Markdown example in the GLFM `spec.txt`. These example snapshots
+ support the following usages for every GLFM Markdown example:
+ 1. The backend (Ruby) parser and renderer can convert Markdown to the
+ expected custom static HTML.
+ 1. The frontend (JavaScript) parser and renderer (which includes GitLab custom
+ code and Remark) can convert Markdown to the expected ProseMirror JSON
+ representing a ProseMirror document.
+ 1. The **Content Editor** (which includes the frontend (JavaScript) parser and renderer,
+ and ProseMirror) can convert Markdown to the expected custom WYSIWYG HTML as rendered by ProseMirror.
+ 1. The **Content Editor** can complete a round-trip test, which involves converting
+ from Markdown, to MDAST, to ProseMirror Document, then back to Markdown. It ensures
+ the resulting Markdown is exactly identical, with no differences.
+
+## Implementation
+
+The following set of scripts and files is complex. However, it allows us to meet
+all of the goals listed above, and is carefully designed to meet the following
+implementation goals:
+
+1. Minimize the amount of manual editing, curation, and maintenance of the GLFM specification
+ and related files.
+1. Automate and simplify the process of updating the GLFM specification and related
+ files when there are changes to the upstream CommonMark spec,
+ GFM extensions, or the GLFM extensions.
+1. Support partial or incomplete implementations of the GLFM specification, whether
+ due to in-progress work, bugs, or new future Markdown support, while still
+ performing all functionality for the existing implementations.
+1. Automate, simplify, and support running various tests, including the standard
+ CommonMark conformance tests and GLFM-implementation-specific unit/acceptance
+ Markdown snapshot tests.
+1. Provide a rich set of extensible metadata around all GLFM specification examples
+ to support current and future requirements, such as automated acceptance
+ testing and automated documentation updates.
+
+The documentation on the implementation is split into three sections:
+
+1. [Scripts](#scripts).
+1. [Specification files](#specification-files).
+1. Example snapshot files: These YAML files are used as input data
+ or fixtures to drive the various tests, and are located under
+ `spec/fixtures/glfm/example_snapshots`. All example snapshot files are automatically
+ generated based on the specification files and the implementation of the parsers and renderers.
+ However, they can also be directly edited if necessary, such as to
+ test-drive an incomplete implementation.
+
+### Scripts
+
+These executable scripts perform various tasks related to maintaining
+the specification and running tests. Each script has a shell-executable entry point
+file located under `scripts/glfm`, but the actual implementation is in unit-tested
+classes under `scripts/lib/glfm`.
+
+NOTE:
+Some of these scripts are implemented in Ruby, and others are shell scripts.
+Ruby scripts are used for more complex custom scripts, to enable easier unit testing
+and debugging. Shell scripts are used for simpler scripts which primarily invoke
+other shell commands, to avoid the challenges related to
+[running other shell sub-processes](https://github.com/thewoolleyman/process_helper#why-yet-another-ruby-process-wrapper-library)
+from Ruby scripts.
+
+NOTE:
+The Ruby executable scripts under `scripts/glfm` have dashes instead of underscores
+in the filenames. This naming is non-standard for a Ruby file, but is used to distinguish
+them from the corresponding implementation class entry point files under
+`scripts/lib/glfm` when searching by filename.
+
+#### `update-specification.rb` script
+
+The `scripts/glfm/update-specification.rb` script uses specification input files to
+generate and update `spec.txt` (Markdown) and `spec.html` (HTML). The `spec.html` is
+generated by passing the generated (or updated) `spec.txt` Markdown to the backend API
+for rendering to static HTML:
+
+```mermaid
+graph LR
+subgraph script:
+ A{update-specification.rb}
+ A --> B{Backend Markdown API}
+end
+subgraph input:<br/>input specification files
+ C[gfm_spec_v_0.29.txt] --> A
+ D[glfm_intro.txt] --> A
+ E[glfm_canonical_examples.txt] --> A
+end
+subgraph output:<br/>GLFM specification files
+ A --> F[spec.txt]
+ F --> B
+ B --> G[spec.html]
+end
+```
+
+#### `update-example-snapshots.rb` script
+
+The `scripts/glfm/update-example-snapshots.rb` script uses input specification
+files to update example snapshots:
+
+```mermaid
+graph LR
+subgraph script:
+ A{update-example-snapshots.rb}
+end
+subgraph input:<br/>input specification files
+ B[downloaded gfm_spec_v_0.29.txt] --> A
+ C[glfm_canonical_examples.txt] --> A
+ D[glfm_example_status.yml] --> A
+end
+subgraph output:<br/>example snapshot files
+ A --> E[examples_index.yml]
+ A --> F[markdown.yml]
+ A --> G[html.yml]
+ A --> H[prosemirror_json.yml]
+end
+```
+
+#### `run-snapshot-tests.sh` script
+
+The `scripts/glfm/run-snapshot-tests.sh` convenience shell script runs all relevant
+Markdown snapshot testing RSpec and Jest `*_spec` files (from main app `spec` folder)
+which are driven by `example_snapshot` YAML files.
+
+The actual RSpec and Jest test `*_spec` files (frontend and backend) live
+under the normal relevant locations under `spec`, matching the location of their
+corresponding implementations. They can be run either:
+
+- As part of the normal pipelines.
+- From the command line or an IDE, just like any other file under `spec`.
+
+However, they are spread across four different locations:
+
+- Backend tests under `spec/requests`.
+- Backend EE tests under `ee/spec/requests`.
+- Frontend tests under `spec/frontend`.
+- Frontend EE tests under `ee/spec/frontend`.
+
+Therefore, this convenience script is intended to only be used in local
+development. It simplifies running all tests at once and returning a single return
+code. It contains only shell scripting commands for the relevant
+`bundle exec rspec ...` and `yarn jest ...` commands.
+
+```mermaid
+graph LR
+subgraph script:
+ A{run-snapshopt-tests.sh} --> B
+ B[relevant rspec/jest test files]
+end
+subgraph input:<br/>YAML
+ C[examples_index.yml] --> B
+ D[markdown.yml] --> B
+ E[html.yml] --> B
+ F[prosemirror_json.yml] --> B
+end
+subgraph output:<br/>test results/output
+ B --> G[rspec/jest output]
+end
+```
+
+#### `canonicalize-html.rb` script
+
+The `scripts/glfm/canonicalize-html.rb` handles the
+["canonicalization" of HTML](#canonicalization-of-html). It is a pipe-through
+helper script which takes as input a static or WYSIWYG HTML string containing
+extra HTML, and outputs a canonical HTML string.
+
+It is implemented as a standalone, modular, single-purpose script, based on the
+[Unix philosophy](https://en.wikipedia.org/wiki/Unix_philosophy#:~:text=The%20Unix%20philosophy%20emphasizes%20building,developers%20other%20than%20its%20creators.).
+It's easy to use when running the standard CommonMark `spec_tests.py`
+script, which expects canonical HTML, against the GitLab renderer implementations.
+
+#### `run-spec-tests.sh` script
+
+`scripts/glfm/run-spec-tests.sh` is a convenience shell script which runs
+conformance specs via the CommonMark standard `spec_tests.py` script,
+which uses the `glfm_specification/output/spec.txt` file and `scripts/glfm/canonicalize-html.rb`
+helper script to test the GLFM renderer implementations' support for rendering Markdown
+specification examples to canonical HTML.
+
+```mermaid
+graph LR
+subgraph scripts:
+ A{run-spec-tests.sh} --> C
+ subgraph specification testing process
+ B[canonicalize-html.sh] --> C
+ C[spec_tests.py]
+ end
+end
+subgraph input
+ D[spec.txt GLFM specification] --> C
+ E((GLFM static<br/>renderer implementation)) --> B
+ F((GLFM WYSIWYG<br/>renderer implementation)) --> B
+end
+subgraph output:<br/>test results/output
+ C --> G[spec_tests.py output]
+end
+```
+
+### Specification files
+
+These files represent the GLFM specification itself. They are all
+located under the root `glfm_specification`, and are further divided into two
+subfolders:
+
+- `input`: Contains files which are imported or manually edited.
+- `output`: Contains files which are automatically generated.
+
+#### Input specification files
+
+The `glfm_specification/input` directory contains files which are the original
+input to drive all other automated GLFM specification scripts/processes/tests.
+They are either downloaded, as in the case of the
+GFM `spec.txt` file, or manually
+updated, as in the case of all GFM files.
+
+- `glfm_specification/input/github_flavored_markdown/gfm_spec_v_0.29.txt` -
+ official latest [GFM spec.txt](https://github.com/github/cmark-gfm/blob/master/test/spec.txt),
+ automatically downloaded and updated by `update-specification.rb` script.
+- `glfm_specification/input/gitlab_flavored_markdown/glfm_intro.txt` -
+ Manually updated text of intro section for generated GLFM `spec.txt`.
+ - Replaces GFM version of introductory
+ section in `spec.txt`.
+- `glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt` -
+ Manually updated canonical Markdown+HTML examples for GLFM extensions.
+ - Standard backtick-delimited `spec.txt` examples format with Markdown + canonical HTML.
+ - Inserted as a new section before the appendix of generated `spec.txt`.
+- `glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml` -
+ Manually updated status of automatic generation of files based on Markdown
+ examples.
+ - Allows example snapshot generation, Markdown conformance tests, or
+ Markdown snapshot tests to be skipped for individual examples. For example, if
+ they are unimplemented, broken, or cannot be tested for some reason.
+
+`glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml` sample entry:
+
+```yaml
+07_99_an_example_with_incomplete_wysiwyg_implementation_1:
+ skip_update_example_snapshots: true
+ skip_running_snapshot_static_html_tests: false
+ skip_running_snapshot_wysiwyg_html_tests: true
+ skip_running_snapshot_prosemirror_json_tests: true
+ skip_running_conformance_static_tests: false
+ skip_running_conformance_wysiwyg_tests: true
+```
+
+#### Output specification files
+
+The `glfm_specification/output` directory contains the CommonMark standard format
+`spec.txt` file which represents the canonical GLFM specification which is generated
+by the `update-specification.rb` script. It also contains the rendered `spec.html`
+and `spec.pdf` which are generated from with the `spec.txt` as input.
+
+- `glfm_specification/output/spec.txt` - A Markdown file, in the standard format
+ with prose and Markdown + canonical HTML examples, generated (or updated) by the
+ `update-specification.rb` script.
+- `glfm_specification/output/spec.html` - An HTML file, rendered based on `spec.txt`,
+ also generated (or updated) by the `update-specification.rb` script at the same time as
+ `spec.txt`. It corresponds to the HTML-rendered versions of the
+ "GitHub Flavored Markdown" (<abbr title="GitHub Flavored Markdown">GFM</abbr>)
+ [specification](https://github.github.com/gfm/)
+ and the [CommonMark specification](https://spec.commonmark.org/0.30/).
+
+These output `spec.**` files, which represent the official, canonical GLFM specification
+are colocated under the same parent folder `glfm_specification` with the other
+`input` specification files. They're located here both for convenience, and because they are all
+a mix of manually edited and generated files. In GFM,
+`spec.txt` is [located in the test dir](https://github.com/github/cmark-gfm/blob/master/test/spec.txt),
+and in CommonMark it's located
+[in the project root](https://github.com/github/cmark-gfm/blob/master/test/spec.txt).
+No precedent exists for a standard location. In the future, we may decide to
+move or copy a hosted version of the rendered HTML `spec.html` version to another location or site.
+
+### Example snapshot files
+
+The `example_snapshots` directory contains files which are generated by the
+`update-example-snapshots.rb` script based off of the files in the
+`glfm_specification/input` directory. They are used as fixtures to drive the
+various Markdown snapshot tests.
+
+After the entire GLFM implementation is complete for both backend (Ruby) and
+frontend (JavaScript), all of these YAML files can be automatically generated.
+However, while the implementations are still in progress, the `skip_update_example_snapshots`
+key in `glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml`
+can be used to disable automatic generation of some examples, and they can instead
+be manually edited as necessary to help drive the implementations.
+
+#### `spec/fixtures/glfm/example_snapshots/examples_index.yml`
+
+`spec/fixtures/glfm/example_snapshots/examples_index.yml` is the main list of all
+CommonMark, GFM, and GLFM example names, each with a unique canonical name.
+
+- It is generated from the hierarchical sections and examples in the
+ GFM `spec.txt` specification.
+- For CommonMark and GFM examples,
+ these sections originally came from the GFM `spec.txt`.
+- For GLFM examples, it is generated from `glfm_canonical_examples.txt`, which is
+ the additional Section 7 in the GLFM `spec.txt`.
+- It also contains extra metadata about each example, such as:
+ 1. `spec_txt_example_position` - The position of the example in the generated GLFM `spec.txt` file.
+ 1. `source_specification` - Which specification the example originally came from:
+ `commonmark`, `github`, or `gitlab`.
+- The naming convention for example entry names is based on nested header section
+ names and example index within the header.
+ - This naming convention should result in fairly stable names and example positions.
+ The CommonMark / GLFM specification rarely changes, and most GLFM
+ examples where multiple examples exist for the same Section 7 subsection are
+ added to the end of the sub-section.
+
+`spec/fixtures/glfm/example_snapshots/examples_index.yml` sample entries:
+
+```yaml
+02_01_preliminaries_characters_and_lines_1:
+ spec_txt_example_position: 1
+ source_specification: commonmark
+03_01_blocks_and_inlines_precedence_1:
+ spec_txt_example_position: 12
+ source_specification: commonmark
+05_03_container_blocks_task_list_items_1:
+ spec_txt_example_position: 279
+ source_specification: github
+06_04_inlines_emphasis_and_strong_emphasis_1:
+ spec_txt_example_position: 360
+ source_specification: github
+07_01_audio_link_1:
+ spec_txt_example_position: 301
+ source_specification: gitlab
+```
+
+#### `spec/fixtures/glfm/example_snapshots/markdown.yml`
+
+`spec/fixtures/glfm/example_snapshots/markdown.yml` contains the original Markdown
+for each entry in `spec/fixtures/glfm/example_snapshots/examples_index.yml`
+
+- For CommonMark and GFM Markdown,
+ it is generated (or updated) from the standard GFM
+ `spec.txt` using the `update-example-snapshots.rb` script.
+- For GLFM, it is generated (or updated) from the
+ `glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt`
+ input specification file.
+
+`spec/fixtures/glfm/example_snapshots/markdown.yml` sample entry:
+
+```yaml
+06_04_inlines_emphasis_and_strong_emphasis_1: |-
+ *foo bar*
+```
+
+#### `spec/fixtures/glfm/example_snapshots/html.yml`
+
+`spec/fixtures/glfm/example_snapshots/html.yml` contains the HTML for each entry in
+`spec/fixtures/glfm/example_snapshots/examples_index.yml`
+
+Three types of entries exist, with different HTML for each:
+
+- **Canonical**
+ - The ["Canonical"](#canonicalization-of-html) HTML.
+ - For CommonMark and GFM examples, the HTML comes from the examples in `spec.txt`.
+ - For GLFM examples, it is generated/updated from
+ `glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt`.
+- **Static**
+ - This is the static (backend (Ruby)-generated) HTML for each entry in
+ `spec/fixtures/glfm/example_snapshots/examples_index.yml`.
+ - It is generated/updated from backend [Markdown API](../../../api/markdown.md)
+ (or the underlying internal classes) via the `update-example-snapshots.rb` script,
+ but can be manually updated for static examples with incomplete implementations.
+- **WYSIWYG**
+ - The WYSIWYG (frontend, JavaScript-generated) HTML for each entry in
+ `spec/fixtures/glfm/example_snapshots/examples_index.yml`.
+ - It is generated (or updated) from the frontend Content Editor implementation via the
+ `update-example-snapshots.rb` script. It can be manually updated for WYSIWYG
+ examples with incomplete implementations.
+
+Any exceptions or failures which occur when generating HTML are replaced with an
+`Error - check implementation` value.
+
+`spec/fixtures/glfm/example_snapshots/html.yml` sample entry:
+
+```yaml
+06_04_inlines_emphasis_and_strong_emphasis_1:
+ canonical: |-
+ <p><em>foo bar</em></p>
+ static: |-
+ <p data-sourcepos="1:1-1:9" dir="auto"><strong>foo bar</strong></p>
+ wysiwyg: |-
+ <p><strong>foo bar</strong></p>
+```
+
+NOTE:
+The actual `static` or `WYSIWYG` entries may differ from the example `html.yml`,
+depending on how the implementations evolve.
+
+#### `spec/fixtures/glfm/example_snapshots/prosemirror_json.yml`
+
+`spec/fixtures/glfm/example_snapshots/prosemirror_json.yml` contains the ProseMirror
+JSON for each entry in `spec/fixtures/glfm/example_snapshots/examples_index.yml`
+
+- It is generated (or updated) from the frontend code via the `update-example-snapshots.rb`
+ script, but can be manually updated for examples with incomplete implementations.
+- Any exceptions or failures when generating are replaced with a `Error - check implementation` value.
+
+`spec/fixtures/glfm/example_snapshots/prosemirror_json.yml` sample entry:
+
+```yaml
+06_04_inlines_emphasis_and_strong_emphasis_1: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "bold"
+ }
+ ],
+ "text": "foo bar"
+ },
+ ]
+ },
+ ]
+ }
+```