Skip to content
Marcos Cáceres edited this page Nov 8, 2021 · 58 revisions

Clone the repo and install the needed dependencies:

git clone git@github.com:w3c/respec.git
npm install

Now you can start the local development servers:

npm start -- --browser Chrome

Note: You can use Firefox and ChromeCanary in the above.

That will start up "Karma" and a local http server for you.

Open the url given (usually http://127.0.0.1:8000). And go to "examples".

Usually "basic.html" is a good place to start hacking from.

ReSpec's architecture

ReSpec is an application that runs mostly synchronous bits of JS after a Document loads. These JavaScript fragments are referred to as "plugins". When a bunch of plugins are combined together, they create a "profile".

Generally, a "profile" is only created for a real-world organization or company. So, for instance, the W3C's profile, located at profiles/w3c.js, loads the following plugins (not the full list, just for illustrative purposes):

  • core/base-runner
  • core/include-config,
  • core/style,
  • w3c/style,
  • core/markdown,
  • w3c/headers,
  • ...

What each plugin above does doesn't matter, though you can deduce what each does by the name. What matters is that ordering is important - and we mix together W3C plugins and "core" plugins. And that it's these plugins coming together that form a profile, in this case the "W3C profile". Each of the plugins are run only once - and the thing that runs them is the core/base-runner plugin.

See profile/w3c.js for the actual details of how the profile is set up. But it's essentially:

  1. Load the profile + all the plugins (but don't "run" them yet!).
  2. Wait for the document's "DOMContentLoaded" event to fire.
  3. Once DOM is ready, run each plugin in order, waiting for each plugin to finish.

Core Base runner (core/base-runner.js)

The first and most important plugin (core/base-runner), is actually the "brains" of ReSpec: it is the thing that "runs" all other plugins in order.

Before any plugins are run, however, it adds the following property to the document object:

// The following only resolves once all plugins have run
// and ReSpec has finished doing whatever it needs to do.
document.respec.isReady;

After that, the Base Runner starts looping over an array of given plugins: literally just a call to a .runAll(arrayOfPlugins) method. For each plugin, it waits until a plugin has finished doing its work before continuing to the next plugin. It does this by calling the run() function exported from a plugin, and awaiting for that function to finish. Some plugins may export a Plugin class with a run() method instead.

Once all the plugins have "run", ReSpec resolves the respec.isReady promise on the Document object.

document.respec.isReady.then(() => {
  console.log("ReSpec has finished processing this document");
});

This is potentially useful for scripts that depend on ReSpec's output. They can wait for the promise to settle before doing their own work.

Alternatively, if you really need to run things immediately before or after ReSpec runs the plugins, you can define preProcess or postProcess properties on the configuration object. See preProcess and postProcess more details and for examples.

Plugins

Plugins are simple ES6 modules that live in the "src/" folder. They have two parts: A synchronous initialization, and an optionally exported run() function that is called asynchronously.

A plugin looks like this:

// import other things you need
import utils from "core/utils";

// This part runs synchronously and an indeterminate order.
// do any plugin specific setup here. Note, the document
// can be in an unstable state here - so don't manipulate
// the DOM here!

// Optionally, export "run" function
// See below for description of arguments.
export async function run(conf) {
  if ("something" in conf || document.querySelector("#important-element")) {
    await someAsyncTask();
  }
}

async function someAsyncTask() {
  // Your code here
}

The exported run method SHOULD have arguments (conf):

  • conf: is the ReSpec configuration object (window.respecConfig) - which the user defined. Be careful not to modify this object.

Warning users of errors

If you are creating a plugin that needs to show warnings to a user, you can use the "core/pubsubhub" utility. As the name suggests, this is a simple pub-sub-hub dispatcher. You should use this to dispatch "warn" or "error" messages to a user:

import { pub } from "./core/pubsubhub.js";
export async function run(conf) {
  if (!"something" in conf) {
    pub("warn", "Markdown that represents a warning");
  }
}

These "warn" and "error" messages will be picked up by ReSpec's UI (the "pill"), and displayed to the end-user. You should only "error" on things that the user needs to fix to successfully publish their document. Likewise, only warn on things the user SHOULD fix.

IMPORTANT: Don't show JavaScript errors to the user - as they won't be able to fix these, and the minified JS output will make these messages really unhelpful!

npm start

The start script in package.json contains the commands useful during development. It runs a static HTTP server, watches files for change and re-build the profile, and run unit tests.

Built-in HTTP server

You can launch a built in HTTP server during development by simply typing: npm start. If you wish not to run tests and other parts of start script, you can alternatively run npm run server.

Testing

ReSpec's unit tests are written using Jasmine and run on Karma. To start the testing server:

npm start -- --browser Firefox

You can run test in different browsers by setting browsers value above to any of: Firefox, FirefoxHeadless, Chrome, ChromeHeadless, Safari. Same can be set using the BROWSERS environment variable:

BROWSERS="ChromeHeadless Firefox" npm start

For debugging purposes, you can click on the Debug button when the tests start in the browser - this will allow you to see the tests summary in browser itself as well as allow you to re-run any particular test.

Please refer to Jasmine documentation regarding focused specs (fdescribe(), fit()) to see how to run only specific tests when running npm run karma. This will save you a lot of time and pain.

You can also select individual tests by filtering those which match a particular pattern:

npm start -- --grep="SEO"

If you want to run all tests whose description includes "SEO".

Interactive mode

You can also run start in "interactive" mode. This gives you more control over when tests are run and, by default, turns off automatic file watching.

npm start -- --interactive

This is useful for more advanced debugging sessions, and can be combined with --grep to test just what you want, when you want.

Testing without opening browser window

You can also run tests without opening a full browser window. Test results will be visible in your terminal.

npm start -- --browser FirefoxHeadless
# or use ChromeHeadless

Look at the help dialog when you run npm start for more options.

Custom profiles

If you are a company, standards consortium, or government entity, you might want to consider maintaining your own ReSpec profile. That allows you have your own content templates, load whatever plugins you need, and generally keep control over how ReSpec runs.

To create a custom profile:

  1. Make a copy of "profiles/w3c.js", but rename it "YOUR-PROFILE-NAME.js".
  2. Open "YOUR-PROFILE-NAME.js", and remove, add, etc. any plugins you want.
  3. run: node ./tools/builder.js YOUR-PROFILE-NAME. That will generate a bundle in the build directory.

If the profile is popular, then please send a pull request to the main repository and we can host as part of the main project.

Working with your new profile

In examples/, make a copy of "basic.html" and point the <script> tag at your new profile. Now run:

npm start -- --profile YOUR_PROFILE_NAME --browser Chrome

That will start a web server, so you can now load up http://localhost:8000/examples and have play with your custom profile.

Testing your profile

If you are writing custom Jasmine tests, simply place them into tests/spec/YOUR-PROFILE-NAME/. And then run:

npm start -- --interactive --profile=YOUR-PROFILE-NAME --browser Chrome

If you prefer to use a different browser, that's ok too.

Guides

Configuration options

W3C Configuration options

Linter rules

Internal properties

Handled by ReSpec for you.

Special <section> IDs

HTML elements

Custom Elements

WebIDL

HTML attributes

CSS Classes

Special properties

Clone this wiki locally