-
-
Notifications
You must be signed in to change notification settings - Fork 389
Developers Guide
As a first step, clone the repository:
git clone git@github.com:speced/respec.git
Developing ReSpec requires Node.js v18.14+ and pnpm
v8+. You can "install" pnpm
with corepack
as:
corepack enable
corepack prepare --activate # run this from repository root
and install the needed dependencies:
pnpm install
Now you can start the local development servers:
pnpm 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 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:
- Load the profile + all the plugins (but don't "run" them yet!).
- Wait for the document's "DOMContentLoaded" event to fire.
- Once DOM is ready, run each plugin in order, waiting for each plugin to finish.
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.ready;
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 await
ing 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.ready
promise on the Document object.
document.respec.ready.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 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.
If you are creating a plugin that needs to show warnings to a user, you can use the showWarning
utility.
import { showWarning, showError } from "./core/utils.js";
export async function run(conf) {
if (!"something" in conf) {
showWarning("Some error message", "plugin-name");
// You can pass additional details like a `hint` how to fix, list of `elements` that caused the issue etc.
// See showWarning and showError in core/utils.js
}
}
These 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!
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.
You can launch a built in HTTP server during development by simply typing: pnpm start
.
If you wish not to run tests and other parts of start script, you can alternatively run pnpm run server
.
ReSpec's unit tests are written using Jasmine and run on Karma. To start the testing server:
pnpm 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" pnpm 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 pnpm 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:
pnpm start --grep="SEO"
If you want to run all tests whose description includes "SEO".
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.
pnpm 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.
You can also run tests without opening a full browser window. Test results will be visible in your terminal.
pnpm start --browser FirefoxHeadless
# or use ChromeHeadless
Look at the help dialog when you run pnpm start
for more options.
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:
- Make a copy of "profiles/w3c.js", but rename it "YOUR-PROFILE-NAME.js".
- Open "YOUR-PROFILE-NAME.js", and remove, add, etc. any plugins you want.
- 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.
In examples/
, make a copy of "basic.html" and point the <script>
tag at your new profile. Now run:
pnpm 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.
If you are writing custom Jasmine tests, simply place them into tests/spec/YOUR-PROFILE-NAME/
. And then run:
pnpm start --interactive --profile=YOUR-PROFILE-NAME --browser Chrome
If you prefer to use a different browser, that's ok too.
💖 Support ReSpec by becoming a sponsor via Open Collective. 💖
✨ View rendered version of this documentation at https://respec.org/docs/ ✨
- addSectionLinks
- authors
- caniuse
- edDraftURI
- editors
- favicon
- format (markdown)
- formerEditors
- github
- highlightVars
- isPreview
- license
- lint
- localBiblio
- logos
- maxTocLevel
- mdn
- modificationDate
- noTOC
- otherLinks
- pluralize
- postProcess
- preProcess
- previousDiffURI
- previousMaturity
- previousPublishDate
- prevRecShortname
- prevRecURI
-
processVersion(Deprecated) - publishDate
-
refNote(Deprecated) - shortName
- specStatus
- subjectPrefix
- subtitle
- testSuiteURI
- xref
- additionalCopyrightHolders
-
addPatentNote(Deprecated) - alternateFormats
- canonicalURI
- charterDisclosureURI
- copyrightStart
- crEnd
-
darkMode(deprecated, use dark mode) - doJsonLd
- errata
- group
- implementationReportURI
- lcEnd
- level
- noRecTrack
- prevED
- submissionCommentNumber
-
wg(Deprecated) -
wgId(Deprecated) -
wgPatentPolicy(Deprecated) -
wgPatentURI(Deprecated) - wgPublicList
-
wgURI(Deprecated)
a11y
check-punctuation
local-refs-exist
no-headingless-sections
no-http-props
no-unused-vars
no-unused-dfns
informative-dfn
privsec-section
wpt-tests-exist
Handled by ReSpec for you.
- data-abbr
-
data-cite(Not recommended) - data-dfn-for
- data-dfn-type
- data-format
- data-include-format
- data-include-replace
- data-include
- data-link-for
- data-link-type
- data-local-lt
- data-lt-no-plural
- data-lt-noDefault
- data-lt
- data-number
- data-oninclude
- data-sort
- data-tests
-
data-transform(Deprecated) - data-type
- dir
- lang