Skip to content

Commit 16a7ade

Browse files
committed
Update docs
1 parent 370a9c0 commit 16a7ade

File tree

5 files changed

+333
-94
lines changed

5 files changed

+333
-94
lines changed

docs/src/buildtools/1-devenv.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,8 @@ yarn workspaces focus @sourceacademy/modules-buildtools
1515
Compiling the build tools is as simple as running `yarn build`. This by default produces a minified Javascript file, which is not very conducive to debugging. If necessary
1616
you can run `yarn build --dev` instead to produce a non-minified build.
1717

18+
For the projects that are supposed to be bundled into a single file using `esbuild`, the package `@sourceacademy/lib-compiler` wraps `esbuild` and `commander` to create a
19+
easy to use compiler.
20+
1821
## Testing
1922
The build tools come with a comprehensive suite of tests. If you are planning to contribute, please write tests to cover your new/modified functionality.

docs/src/buildtools/8-testing.md

Lines changed: 105 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
11
# Testing Using `Vitest`
22

33
`vitest` comes with its own [Node API](https://vitest.dev/advanced/api/) that can be used to run tests from Node. To reduce the number of configuration files required,
4-
the buildtools provide their own default `vitest` configuration for bundles and tabs.
4+
the buildtools provide their own default `vitest` configuration for bundles and tabs. `vitest` supports a similar concept to workspaces known as [projects](https://vitest.dev/guide/projects.html).
55

6-
`vitest` supports a similar concept to workspaces known as [projects](https://vitest.dev/guide/projects.html). However, each `vitest` configuration file assumes that it isn't a child project under another root configuration.
7-
So, when we use `mergeConfig` to inherit test configuration options from the root config, `vitest` tries to resolve `projects` field relative to that file. For example, if we have the following `vitest.config.ts`:
6+
::: details `vitest`'s own configuration utilities
7+
We can combine configurations using the `mergeConfig` helper from `vitest/config`.
8+
This helper doesn't do anything interesting other than directly combine the options between the two parameters.
89

9-
```ts
10-
// src/vitest.config.ts
11-
import { defineConfig } from 'vitest/config';
10+
This means that if we attached the projects configuration to the root config file, the project configuration will end up
11+
getting copied over to every other child configuration.
12+
13+
For example, consider the root config file below with one project configured.
1214

15+
```js
16+
import { defineConfig } from 'vitest/config';
17+
// Root Config
1318
export default defineConfig({
1419
test: {
15-
name: 'Root Test Project',
16-
projects: [
17-
'src/bundles/*/vitest.config.ts'
18-
]
20+
projects: ['./sub-project/vitest.config.js']
1921
}
2022
})
2123
```
22-
23-
And then we have a child config that inherits from the root config:
24-
```ts
25-
// src/bundles/curve/vitest.config.ts
26-
24+
For the child configuration:
25+
```js
2726
import { defineProject, mergeConfig } from 'vitest/config';
28-
import rootConfig from '../../vitest.config.ts';
27+
import rootConfig from '../vitest.config.js';
2928

3029
export default mergeConfig(
3130
rootConfig,
@@ -37,52 +36,106 @@ export default mergeConfig(
3736
)
3837
```
3938

40-
If we run `vitest` from the root of the repository, it will accurately detect `"Child Project"` is a valid project name. However, the buildtools need to be able to be executed from within the bundle's directory too.
41-
42-
If we run the following command within the bundles directory, we will find that `vitest` cannot find a project with the name "Child Project":
43-
44-
```sh
45-
yarn vitest --project "Child Project"
46-
```
47-
48-
This can be solved by configuring the root `vitest.config.ts`'s root parameter:
49-
50-
```ts {6}
51-
// src/vitest.config.ts
52-
import { defineConfig } from 'vitest/config';
53-
54-
export default defineConfig({
39+
The actual configuration that's actually resolved by `vitest` actually looks like this:
40+
```js
41+
const config = {
5542
test: {
56-
root: import.meta.dirname,
57-
name: 'Root Test Project',
58-
projects: [
59-
'src/bundles/*/vitest.config.ts'
60-
]
43+
name: 'Child Project',
44+
projects: ['./sub-project/vitest.config.js']
6145
}
62-
})
46+
}
47+
```
48+
So if you ran `yarn vitest --config ./sub-project/vitest.config.js`, `vitest` would try to locate another `vitest` project
49+
located at `./sub-project/sub-project/vitest.config.js`. Since the child config is intended to be just that, this is simply
50+
incorrect functionality. If that particular configuration file doesn't exist, `vitest` wouldn't even be able to start.
51+
52+
In the spirit of more closely matching the projects configuration system that `jest` uses, a lot of code had to be written
53+
to get around the way `vitest` behaves.
54+
:::
55+
56+
The buildtools maintains a set of default configurations. If no `vitest` configuration is present, then those defaults are used automatically.
57+
Otherwise, that configuration is loaded and its options are combined with the default configurations using `mergeConfig`.
58+
59+
## Config Resolution
60+
```mermaid
61+
graph TD
62+
A[Does this directory contain a Vitest config?]
63+
B[Load the config and combine it with defaults]
64+
C[Is this directory a tab or a bundle?]
65+
E[Load defaults]
66+
D[Return Error]
67+
I[Does this bundle or tab have tests?]
68+
F[Is browser mode enabled]
69+
G[Process Browser Mode Options]
70+
H[Run Vitest]
71+
J[Skip directory]
72+
73+
A -- Yes --> B
74+
A -- No --> C
75+
C -- Yes --> I
76+
I -- No --> J
77+
I -- Yes --> E
78+
C -- No --> D
79+
80+
B --> F
81+
E --> F
82+
83+
F -- Yes --> G
84+
F -- No --> H
85+
G --> H
6386
```
64-
This causes `vitest` to resolve the configuration correctly every time.
6587

66-
## Vitest for specific Bundle/Tab
88+
### `testall` Command
89+
The `testall` command functions slightly differently. The command first checks the root `vitest.config.js` to identify which directories it should check as a `vitest` project.
90+
For each of those directories, no error will be thrown if the `vitest` config file is missing, unless that directory also contains test files.
6791

68-
However, even with the configuration above, we face an issue. `vitest` requires that each project specification matches a specific file. The pattern `src/bundles/*/vitest.config.ts` matches all `vitest.config.ts` files under the
69-
top-level directory of each bundle. This is equivalent to `src/bundles/*`, since by default, `vitest` will try to find a file named `vitest.config`. This requires that the specific configuration file exists in that folder for it to be considered a project.
92+
Bundles and tabs are allowed to be missing a `vitest` configuration file and still have tests, since the buildtools will automatically provide a default `vitest` configuration.
7093

71-
This means that for a bundle/tab's tests to be detected, that bundle or tab would need to maintain its own `vitest` config. Alternatively, the specific configuration for that bundle/tab could be added directly to the root config. Neither of these solutions are particularly ideal.
72-
The former would run the risk of misconfiguration, and the latter would cause the root configuration to become cluttered and defeat the purpose of splitting the repository into workspaces.
94+
## Actually Running `vitest` from Node
95+
The main way to run `vitest` from Node is via the [`startVitest`](https://vitest.dev/advanced/api/#startvitest) function. The documentation
96+
for this function is unfortunately a bit lacking, but from what I have figured out, here's how it works:
7397

74-
To remove the need for each bundle/tab to contain its own Vitest config, the buildtools provide the configuration manually:
98+
| |Parameter | Type | Explanation |
99+
| - | --------- | --------- | ----------- |
100+
| 1 | Mode | <code>'bench' \| 'test'</code> | Vitest Run Mode. Used to distinguish between running tests and benchmarks |
101+
| 2 | Filters | `string[]` | Glob paths to filter test files by |
102+
| 3 | CLI Options | `VitestUserConfig` | Your actual test options. |
103+
| 4 | Vite Options | `ViteUserConfig` | Options for Vite |
104+
| 5 | Vitest Options | `VitestOptions` | A configuration object for `stdout`, `stderr` and the like. |
75105

76-
<<< ../../../lib/buildtools/src/testing.ts
106+
That third parameter should be passed a test configuration, rather than the entire Vite config:
107+
```js
108+
const fullViteConfig = {
109+
test: {
110+
name: 'Tests'
111+
}
112+
}
77113

78-
Within the `runIndividualVitest` function, the bundle/tab you are trying to run tests for gets configured as its own test project that inherits options from the root configuration file.
114+
// Pass this to startVitest instead
115+
const vitestConfig = {
116+
name: 'Tests'
117+
}
118+
```
79119

80-
With this, running `yarn test` within a bundle or tab's directory structure will only run the tests for that tab or bundle without requiring the `--project` or directory filters.
120+
You should also pass `config: false`. Otherwise, `vitest` will still try to load a `vitest` configuration file
121+
using its own resolution rules:
122+
```js
123+
startVitest('test', [], {
124+
config: false,
125+
...testOptions
126+
})
127+
```
128+
When running tests, you need to actually provide each configuration object as a project:
81129

82-
## Vitest for all bundles or all tabs or both
130+
```js
131+
startVitest('test', [], {
132+
config: false,
133+
projects: [yourTestConfig]
134+
})
135+
```
83136

84-
The approach above works when you know that the current tab or bundle has tests that need to be run. In order to run tests for **all** bundles or **all** tabs at once, the buildtools would have to determine
85-
which bundles and tabs have tests and which don't (as Vitest considers it an error if you give it a test project that it can't find tests for).
137+
## Browser Mode
138+
If browser mode is enabled, the buildtools will automatically reassign the names of projects and the `include` field will be
139+
moved from the `test` object onto the `browser.instance` object.
86140

87-
Instead, all bundles and all tabs are configured as a test project each. Hence, under `src/bundles` and `src/tabs` you can find a `vitest.config.js` that is specific to bundles and tabs respectively. This shifts
88-
the responsibility for determining which bundles and tabs contain tests onto `vitest` itself and so it will not fail (unless for some reason every single bundle and tab test was removed).
141+
Headless mode is always enabled, so long as `vitest` isn't being run in watch mode.

docs/src/lib/lintplugin/2-rules.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Rules Reference
2+
[[toc]]
23

34
## `tab-type`
45

@@ -39,7 +40,7 @@ export default tabHelper({
3940
});
4041
```
4142

42-
## Options
43+
### Options
4344
This rule accepts a configuration array with two elements:
4445
- The first option represents the expected import source. This is by default `@sourceacademy/modules-lib/tabs` but can be changed to whatever is in use
4546
- The second option is the name of the imported helper. This is by default `defineTab`.
@@ -56,3 +57,35 @@ export default tabHelper({
5657
label: 'tab'
5758
});
5859
```
60+
61+
## `region-comment`
62+
This rule enforces that each `// #region` comment is named and paired with a corresponding `// #endregion` comment.
63+
64+
Examples of **correct** code for this rule:
65+
```ts
66+
// #region Region1
67+
export function foo() {}
68+
// #endregion Region1
69+
```
70+
71+
Regions can overlap:
72+
```ts
73+
// #region Region1
74+
// #region Region2
75+
export function foo() {}
76+
// #endregion Region2
77+
// #endregion Region1
78+
```
79+
80+
Examples of **incorrect** code for this rule:
81+
82+
```ts
83+
// Missing name for region
84+
// #region
85+
export function foo() {}
86+
// #endregion
87+
88+
// Missing #region tag
89+
export function bar() {}
90+
// #endregion 1
91+
```

0 commit comments

Comments
 (0)