diff options
Diffstat (limited to 'subprojects/docs/src')
-rw-r--r-- | subprojects/docs/src/develop/contributing.md | 51 | ||||
-rw-r--r-- | subprojects/docs/src/develop/contributing/commands.md | 172 | ||||
-rw-r--r-- | subprojects/docs/src/develop/contributing/ide-setup.md | 94 | ||||
-rw-r--r-- | subprojects/docs/src/develop/contributing/index.md | 59 | ||||
-rw-r--r-- | subprojects/docs/src/plugins/remarkPosix2Windows.ts | 166 |
5 files changed, 491 insertions, 51 deletions
diff --git a/subprojects/docs/src/develop/contributing.md b/subprojects/docs/src/develop/contributing.md deleted file mode 100644 index 4a135b81..00000000 --- a/subprojects/docs/src/develop/contributing.md +++ /dev/null | |||
@@ -1,51 +0,0 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2021-2023 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 1 | ||
5 | sidebar_label: Contributing | ||
6 | --- | ||
7 | |||
8 | # Contributing to Refinery | ||
9 | |||
10 | ## Setting up the development environment | ||
11 | |||
12 | ### With IntelliJ IDEA | ||
13 | |||
14 | We prefer IntelliJ IDEA as a development environment. | ||
15 | No special preparations should be necessary for importing the project as a Gradle project into IDEA. | ||
16 | |||
17 | However, you will need Eclipse to edit Xtext (`*.xtext`) and MWE2 (`*.mwe2`) files and Ecore class diagrams (`*.aird`, `*.ecore`, `*.genmodel`). | ||
18 | If you do not plan on making changes to such files, feel free to skip the Eclipse installation steps below. | ||
19 | |||
20 | ### With Eclipse IDE | ||
21 | |||
22 | 1. Download and install a _Java 21_ compatible JDK. For Windows, prefer OpenJDK builds from [Adoptium](https://adoptium.net/). | ||
23 | |||
24 | 2. Download and extract the [Eclipse IDE for Java and DSL Developers 2023-12](https://www.eclipse.org/downloads/packages/release/2023-12/r/eclipse-ide-java-and-dsl-developers) package. | ||
25 | |||
26 | 3. Launch Eclipse and create a new workspace. | ||
27 | |||
28 | 4. Open _Help > Eclipse Marketplace_ and install the following software: | ||
29 | * _EclEmma Java Code Coverage_ | ||
30 | * _EcoreTools : Ecore Diagram Editor_ | ||
31 | * _Sirius_ (ignore the warning during installation about the solution _Sirius_ not being available) | ||
32 | * _SonarLint_ | ||
33 | |||
34 | 5. Open _Window > Preferences_ and set the following preferences: | ||
35 | * _General > Workspace > Text file encoding_ should be _UTF-8_. | ||
36 | * _General > Workspace > New text file line delimiter_ should be _Unix_. | ||
37 | * Add the JDK 21 to _Java > Installed JREs_. | ||
38 | * Make sure JDK 21 is selected for _JavaSE-21_ at _Java > Installed JREs > Execution Environments_. | ||
39 | * Set _Gradle > Java home_ to the `JAVA_HOME` directory (the directory which contains the `bin` directory) of JDK 21. Here, Buildship will show a yellow warning sign, which can be safely ignored. | ||
40 | * Set _Java > Compiler > JDK Compliance > Compiler compliance level_ to _21_. | ||
41 | |||
42 | 6. Clone the project Git repository but do not import it into Eclipse yet. | ||
43 | |||
44 | 7. Open a new terminal an run `./gradlew prepareEclipse` (`.\gradlew prepareEclipse` on Windows) in the cloned repository. | ||
45 | * This should complete without any compilation errors. | ||
46 | * If you get any errors about the JVM version, check whether the `JAVA_HOME` environment variable is set to the location of JDK. You can query the variable with `echo $JAVA_HOME` on Linux and `echo $Env:JAVA_HOME` in PowerShell on Windows. To set it, use `export JAVA_HOME=/java/path/here` or `$Env:JAVA_HOME="C:\java\path\here"`, respectively. | ||
47 | * If the build fails with a `Host name must not be empty` error, you [might need to remove the empty proxy configuration from your global `gradle.properties` file](https://stackoverflow.com/a/62128323). | ||
48 | |||
49 | 8. Select _File > Import... > Gradle > Existing Gradle Project_ and import the cloned repository in Eclipse. | ||
50 | * Make sure to select the root of the repository (containing this file) as the _Project root directory_ and that the _Gradle distribution_ is _Gradle wrapper_. | ||
51 | * If you have previously imported the project into Eclipse, this step will likely fail. In that case, you should remove the projects from Eclipse, run `git clean -fxd` in the repository, and start over from step 8. | ||
diff --git a/subprojects/docs/src/develop/contributing/commands.md b/subprojects/docs/src/develop/contributing/commands.md new file mode 100644 index 00000000..abfea704 --- /dev/null +++ b/subprojects/docs/src/develop/contributing/commands.md | |||
@@ -0,0 +1,172 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 1 | ||
5 | title: Build commands | ||
6 | --- | ||
7 | |||
8 | # Building from the command line | ||
9 | |||
10 | ## Gradle commands | ||
11 | |||
12 | We use [Gradle](https://gradle.org/) to manage the compilation and tests of Refinery. | ||
13 | |||
14 | Java code is built directly by Gradle. | ||
15 | We use the [frontend-gradle-plugin](https://siouan.github.io/frontend-gradle-plugin/) to manage a [Node.js](https://nodejs.org/en) and [Yarn](https://yarnpkg.com/) installation, which in turn is used to build TypeScript code (including this documentation website). | ||
16 | Typically, Yarn commands are issued by Gradle and you don't need to work with the TypeScript build system directly if you're only working on the Java parts of Refinery. | ||
17 | |||
18 | ### `build` | ||
19 | |||
20 | ```bash posix2windows | ||
21 | ./gradlew build | ||
22 | ``` | ||
23 | |||
24 | Compile all code, run all tests, and produce all build artifacts. | ||
25 | |||
26 | You should run this command before submitting a [Pull request](https://github.com/graphs4value/refinery/pulls) to make sure that all code builds and tests pass on your local machine. | ||
27 | This will also be run by GitHub Actions for each commit or pull requests. | ||
28 | |||
29 | ### `publishToMavenLocal` | ||
30 | |||
31 | |||
32 | ```bash posix2windows | ||
33 | ./gradlew publishToMavenLocal | ||
34 | ``` | ||
35 | |||
36 | Publishes the Refinery Java artifacts to the [Maven local repository](https://www.baeldung.com/maven-local-repository). | ||
37 | |||
38 | Build tools, such as Gradle, will be able to consume such artifacts, which enables you to use the latest version of Refinery -- possibly including your own modification -- in other Java projects. | ||
39 | |||
40 | For example, in Gradle, you may set | ||
41 | |||
42 | ```kotlin title="build.gradle.kts" | ||
43 | repositories { | ||
44 | mavenLocal() | ||
45 | } | ||
46 | |||
47 | dependencies { | ||
48 | implementation("tools.refinery:refinery-generator:0.0.0-SNAPSHOT") | ||
49 | } | ||
50 | ``` | ||
51 | |||
52 | to add a dependency on Refinery to your Java project. | ||
53 | |||
54 | ### `serve` | ||
55 | |||
56 | ```bash posix2windows | ||
57 | ./gradlew serve | ||
58 | ``` | ||
59 | |||
60 | Starts the Refinery backend and web interface on port 1312. | ||
61 | |||
62 | This task is ideal for running the Refinery backend if you don't intend to work on the frontend. | ||
63 | The Refinery frontend TypeScript projects is automatically built before the server starts. | ||
64 | The server will use the latest build output of the frontend as static assets. | ||
65 | |||
66 | The behavior of this task is influenced by the same [environmental variables](/learn/docker#environmental-variables) as the Refinery [Docker container](/learn/docker). | ||
67 | However, the default value of `REFINERY_LISTEN_PORT` is `1312`. | ||
68 | |||
69 | ### `serveBackend` | ||
70 | |||
71 | ```bash posix2windows | ||
72 | ./gradlew serveBackend | ||
73 | ``` | ||
74 | |||
75 | Starts the Refinery backend on port 1312. | ||
76 | |||
77 | This task is ideal for running the Refinery backend if you're working on the frontend. | ||
78 | No static assets will be build. | ||
79 | You'll need to use [`yarnw frontend dev`](#frontend-dev) | ||
80 | |||
81 | Like [`gradlew serve`](#serve), the behavior of this task is influenced by the same [environmental variables](/learn/docker#environmental-variables) as the Refinery [Docker container](/learn/docker). | ||
82 | However, the default value of `REFINERY_LISTEN_PORT` is `1312`. | ||
83 | |||
84 | ## Yarn commands | ||
85 | |||
86 | We provide a `yarnw` wrapper script to invoke the Yarn distribution installed by frontend-gradle-plugin directly. | ||
87 | The following commands can only be run once [`gradlew build`](#build) has installed the necessary Node.js and Yarn packages. | ||
88 | |||
89 | ### `docs dev` | ||
90 | |||
91 | ```bash posix2windows | ||
92 | ./yarn docs dev | ||
93 | ``` | ||
94 | |||
95 | Builds and serves this documentation in development mode on port 3000. | ||
96 | Saved changes to most documentation sources are immediately reflected in the browse without reloading. | ||
97 | |||
98 | You can set the port with the `-p` option, e.g. to use port 1313, use | ||
99 | |||
100 | ```bash posix2windows | ||
101 | ./yarn docs dev -p 1313 | ||
102 | ``` | ||
103 | |||
104 | :::note | ||
105 | |||
106 | Running this command for the first time may generate error messages like | ||
107 | ``` | ||
108 | ERROR failed to read input source map: failed to parse inline source map url | ||
109 | ``` | ||
110 | which can be safely ignored. | ||
111 | |||
112 | ::: | ||
113 | |||
114 | ### `frontend dev` | ||
115 | |||
116 | ```bash posix2windows | ||
117 | ./yarn frontend dev | ||
118 | ``` | ||
119 | |||
120 | Builds and serves the refinery frontend on port 1313. | ||
121 | Saved changes to most source files are immediately reflected in the browser without reload. | ||
122 | |||
123 | Before running this command, you need to start [`gradlew serveBackend`](#servebackend) to provide a backend for the frontend to connect to. | ||
124 | The development server of the frontend will proxy all WebSocket connections to the backend. | ||
125 | |||
126 | The following environmental variables influence the behavior of this command: | ||
127 | |||
128 | #### `REFINERY_LISTEN_HOST` | ||
129 | |||
130 | Hostname to listen at for incoming HTTP connections. | ||
131 | |||
132 | **Default value:** `localhost` | ||
133 | |||
134 | #### `REFINERY_LISTEN_PORT` | ||
135 | |||
136 | TCP port to listen at for incoming HTTP connections. | ||
137 | |||
138 | **Default value:** `1313` | ||
139 | |||
140 | #### `REFINERY_BACKEND_HOST` | ||
141 | |||
142 | Hostname of the Refinery backend. | ||
143 | |||
144 | This should match the `REFINERY_LISTEN_HOST` passed to [`gradlew serveBackend`](#servebackend). | ||
145 | |||
146 | **Default value:** `127.0.0.1` (connect to `localhost` over IPv4 only) | ||
147 | |||
148 | #### `REFINERY_LISTEN_PORT` | ||
149 | |||
150 | TCP port of the Refinery backend. | ||
151 | |||
152 | This should match the `REFINERY_LISTEN_PORT` passed to [`gradlew serveBackend`](#servebackend). | ||
153 | |||
154 | **Default value:** `1312` | ||
155 | |||
156 | #### `REFINERY_PUBLIC_HOST` | ||
157 | |||
158 | Publicly visible hostname of the Refinery instance. | ||
159 | |||
160 | If you use a reverse proxy in front of the development server, you must set this variable. | ||
161 | Otherwise, connections to the development server will fail due to cross-origin protection. | ||
162 | |||
163 | **Default value:** equal to `REFINERY_LISTEN_HOST` | ||
164 | |||
165 | #### `REFINERY_PUBLIC_PORT` | ||
166 | |||
167 | Publicly visible port of the Refinery instance. | ||
168 | |||
169 | If you use a reverse proxy in front of the development server, you must set this variable. | ||
170 | Otherwise, connections to the development server will fail due to cross-origin protection. | ||
171 | |||
172 | **Default value:** equal to `REFINERY_LISTEN_PORT` | ||
diff --git a/subprojects/docs/src/develop/contributing/ide-setup.md b/subprojects/docs/src/develop/contributing/ide-setup.md new file mode 100644 index 00000000..742035e0 --- /dev/null +++ b/subprojects/docs/src/develop/contributing/ide-setup.md | |||
@@ -0,0 +1,94 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2021-2023 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 2 | ||
5 | title: IDE setup | ||
6 | --- | ||
7 | |||
8 | # Setting up the development environment | ||
9 | |||
10 | ## IntelliJ IDEA | ||
11 | |||
12 | We prefer [IntelliJ IDEA](https://www.jetbrains.com/idea/) as a Java development environment. | ||
13 | No special preparations should be necessary for importing the project as a Gradle project into IDEA: | ||
14 | |||
15 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery about obtaining the required JDK version. You'll also need a version of IntelliJ IDEA that supports **Java 21** (version **2023.3** or later). | ||
16 | |||
17 | 2. Clone the project git repository and open it in IntelliJ IDEA. Make sure to _open_ the project instead of creating a _new_ one in the same directory. | ||
18 | |||
19 | 3. IntelliJ IDEA should build and index the project. If there are errors, it is likely that the `JAVA_HOME` was incorrectly set: | ||
20 | * In _Project Structure > Project settings > Project > SDK_, a Java 21 compatible JDK should be selected. | ||
21 | * In _Project Structure > Project settings > Project > Language level_, either _SDK default_ or _21_ should be selected. | ||
22 | * Make sure that each module in _Project Structure > Project settings > Module_ uses the _Project default_ language level in _Sources > Language level_ and the _Project SDK_ in _Dependencies > Module SDK._ | ||
23 | * In _Settings > Gradle settings > Gralde Projects > Gradle_, the _Distribution_ should be set to _Wrapper_ and the _Gradle JVM_ should be set to _Project SDK._ | ||
24 | |||
25 | 4. We recommend installing the latest _SonarLint_ plugin in _Settings > Plugins_ to get real-time code quality analysis in your IDE. | ||
26 | |||
27 | :::note | ||
28 | |||
29 | You'll need [Eclipse](#eclipse) to edit Xtext (`*.xtext`) and MWE2 (`*.mwe2`) files and Ecore class diagrams (`*.aird`, `*.ecore`, `*.genmodel`). | ||
30 | If you do not plan on making changes to such files, feel free to skip the Eclipse installation steps below. | ||
31 | |||
32 | You'll also need [VS Code](#vs-code) to edit the TypeScript code in Refinery. | ||
33 | |||
34 | ::: | ||
35 | |||
36 | ## Eclipse | ||
37 | |||
38 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery about obtaining the required JDK version. | ||
39 | |||
40 | 2. Download and extract the [Eclipse IDE for Java and DSL Developers 2023-12](https://www.eclipse.org/downloads/packages/release/2023-12/r/eclipse-ide-java-and-dsl-developers) package. | ||
41 | |||
42 | 3. Launch Eclipse and create a new workspace. | ||
43 | |||
44 | 4. Open _Help > Eclipse Marketplace_ and install the following software: | ||
45 | * _EclEmma Java Code Coverage_ | ||
46 | * _EcoreTools : Ecore Diagram Editor_ | ||
47 | * _Sirius_ (ignore the warning during installation about the solution _Sirius_ not being available) | ||
48 | * _SonarLint_ | ||
49 | |||
50 | 5. Open _Window > Preferences_ and set the following preferences: | ||
51 | * _General > Workspace > Text file encoding_ should be _UTF-8_. | ||
52 | * _General > Workspace > New text file line delimiter_ should be _Unix_. | ||
53 | * Add the JDK 21 to _Java > Installed JREs_. | ||
54 | * Make sure JDK 21 is selected for _JavaSE-21_ at _Java > Installed JREs > Execution Environments_. | ||
55 | * Set _Gradle > Java home_ to the `JAVA_HOME` directory (the directory which contains the `bin` directory) of JDK 21. Here, Buildship will show a yellow warning sign, which can be safely ignored. | ||
56 | * Set _Java > Compiler > JDK Compliance > Compiler compliance level_ to _21_. | ||
57 | |||
58 | 6. Clone the project Git repository but _do not_ import it into Eclipse yet. | ||
59 | |||
60 | 7. Open a new terminal and run | ||
61 | ```bash posix2windows | ||
62 | ./gradlew prepareEclipse | ||
63 | ``` | ||
64 | in the cloned repository. | ||
65 | * This should complete without any compilation errors. | ||
66 | * To troubleshoot any error, see the [instructions about compiling Refinery](/develop/contributing#compiling). | ||
67 | |||
68 | 8. Select _File > Import... > Gradle > Existing Gradle Project_ and import the cloned repository in Eclipse. | ||
69 | * Make sure to select the root of the repository (containing this file) as the _Project root directory_ and that the _Gradle distribution_ is _Gradle wrapper_. | ||
70 | * If you have previously imported the project into Eclipse, this step will likely fail. In that case, you should remove the projects from Eclipse, run `git clean -fxd` in the repository, and start over from step 8. | ||
71 | |||
72 | ## VS Code | ||
73 | |||
74 | We recommend [VSCodium](https://github.com/VSCodium/vscodium) or [Visual Studio Code](https://code.visualstudio.com/) to work with the parts of Refinery that are written is TypeScript. | ||
75 | |||
76 | 1. See the [required tools](/develop/contributing#required-tools) for compiling Refinery about obtaining the required JDK version. You'll also need a version of IntelliJ IDEA that supports **Java 21** (version **2023.3** or later). | ||
77 | |||
78 | 2. Install the following VS Code extensions: | ||
79 | * _EditorConfig for VS Code_ [[Open VSX](https://open-vsx.org/extension/EditorConfig/EditorConfig)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)] | ||
80 | * _ZipFS - a zip file system_ [[Open VSX](https://open-vsx.org/extension/arcanis/vscode-zipfs)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs)] | ||
81 | * _ESLint_ [[Open VSX](https://open-vsx.org/extension/dbaeumer/vscode-eslint)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)] | ||
82 | * _XState VSCode_ [[Open VSX](https://open-vsx.org/extension/statelyai/stately-vscode)] [[Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=statelyai.stately-vscode)] | ||
83 | |||
84 | 3. Clone the project Git repository but _do not_ import it into VS Code yet. | ||
85 | |||
86 | 4. Run | ||
87 | ```bash posix2windows | ||
88 | ./gradlew installFrontend | ||
89 | ``` | ||
90 | to install all required Node.js tooling. | ||
91 | |||
92 | 5. Open the repository with _Open Folder…_ in VS Code. | ||
93 | * When asked, select that you _Trust_ the folder. | ||
94 | * When asked, enable using the TypeScript and ESLint tooling specified in the repository. | ||
diff --git a/subprojects/docs/src/develop/contributing/index.md b/subprojects/docs/src/develop/contributing/index.md new file mode 100644 index 00000000..aa0bdb2f --- /dev/null +++ b/subprojects/docs/src/develop/contributing/index.md | |||
@@ -0,0 +1,59 @@ | |||
1 | --- | ||
2 | SPDX-FileCopyrightText: 2024 The Refinery Authors | ||
3 | SPDX-License-Identifier: EPL-2.0 | ||
4 | sidebar_position: 1 | ||
5 | title: Contributing | ||
6 | --- | ||
7 | |||
8 | import TabItem from '@theme/TabItem'; | ||
9 | import Tabs from '@theme/Tabs'; | ||
10 | |||
11 | # Contributing to Refinery | ||
12 | |||
13 | You can clone the refinery repository from GitHub at https://github.com/graphs4value/refinery. | ||
14 | If you want to contribute code, we recommend [forking](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) the repository on GitHub so that you can submit a [pull request](https://github.com/graphs4value/refinery/pulls) later. | ||
15 | |||
16 | ## Required tools | ||
17 | |||
18 | Refinery is written in Java and TypeScript. To build Refinery, you'll need a **Java 21** compatible **Java Development Kit (JDK).** We recommend the [Adoptium Java 21 JDK](https://adoptium.net/) or the [Amazon Corretto Java 21 JDK](https://aws.amazon.com/corretto/). | ||
19 | |||
20 | ## Compiling Refinery {#compiling} | ||
21 | |||
22 | To build Refinery, run the command | ||
23 | ```bash posix2windows | ||
24 | ./gradlew build | ||
25 | ``` | ||
26 | in the cloned repository. | ||
27 | |||
28 | This should complete without any compilation errors. | ||
29 | |||
30 | If you get any errors about the JVM version, check whether the `JAVA_HOME` environment variable is set to the location of **JDK 21**. You can query the variable with | ||
31 | <Tabs groupId="posix2windows"> | ||
32 | <TabItem value="posix" label="Linux or macOS"> | ||
33 | ```bash | ||
34 | echo $JAVA_HOME | ||
35 | ``` | ||
36 | </TabItem> | ||
37 | <TabItem value="windows" label="Windows (PowerShell)"> | ||
38 | ```bash | ||
39 | echo $Env:JAVA_HOME | ||
40 | ``` | ||
41 | </TabItem> | ||
42 | </Tabs> | ||
43 | To set the `JAVA_HOME` environmental variable, use | ||
44 | <Tabs groupId="posix2windows"> | ||
45 | <TabItem value="posix" label="Linux or macOS"> | ||
46 | ```bash | ||
47 | export JAVA_HOME=/java/path/here | ||
48 | ``` | ||
49 | </TabItem> | ||
50 | <TabItem value="windows" label="Windows (PowerShell)"> | ||
51 | ```bash | ||
52 | $Env:JAVA_HOME="C:\java\path\here" | ||
53 | ``` | ||
54 | </TabItem> | ||
55 | </Tabs> | ||
56 | |||
57 | If the build fails with a `Host name must not be empty` error, you [might need to remove the empty proxy configuration from your global `gradle.properties` file](https://stackoverflow.com/a/62128323). | ||
58 | |||
59 | For further information, see the [supported build commands](/develop/contributing/commands) and the [instructions for setting up an IDE](/develop/contributing/ide-setup). | ||
diff --git a/subprojects/docs/src/plugins/remarkPosix2Windows.ts b/subprojects/docs/src/plugins/remarkPosix2Windows.ts new file mode 100644 index 00000000..66baca30 --- /dev/null +++ b/subprojects/docs/src/plugins/remarkPosix2Windows.ts | |||
@@ -0,0 +1,166 @@ | |||
1 | /* | ||
2 | * Copyright (c) Facebook, Inc. and its affiliates. | ||
3 | * Copyright (c) 2024 The Refinery Authors | ||
4 | * | ||
5 | * SPDX-License-Identifier: EPL-2.0 | ||
6 | * | ||
7 | * This file is based on | ||
8 | * https://github.com/facebook/docusaurus/blob/e4ecffe41878728acff55a8370bd7440706c02f7/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts | ||
9 | * but was changed to conver shell commands to POSIX to Windows syntax. | ||
10 | */ | ||
11 | |||
12 | import type { Code, Literal } from 'mdast'; | ||
13 | import type { MdxjsEsm, MdxJsxFlowElement } from 'mdast-util-mdx'; | ||
14 | import type { Transformer } from 'unified'; | ||
15 | import type { Node, Parent } from 'unist'; | ||
16 | import { visit } from 'unist-util-visit'; | ||
17 | |||
18 | function isLiteral(node: Node): node is Literal { | ||
19 | return node.type === 'mdxjsEsm'; | ||
20 | } | ||
21 | |||
22 | function isTabImport(node: Node): boolean { | ||
23 | return isLiteral(node) && node.value.includes('@theme/Tabs'); | ||
24 | } | ||
25 | |||
26 | function isParent(node: Node): node is Parent { | ||
27 | return 'children' in node && Array.isArray(node.children); | ||
28 | } | ||
29 | |||
30 | function isCode(node: Node): node is Code { | ||
31 | return node.type === 'code'; | ||
32 | } | ||
33 | |||
34 | function isPosix2Windows(node: Node): node is Code { | ||
35 | return isCode(node) && node.meta === 'posix2windows'; | ||
36 | } | ||
37 | |||
38 | function createTabItem( | ||
39 | code: string, | ||
40 | node: Code, | ||
41 | value: string, | ||
42 | label: string, | ||
43 | ): MdxJsxFlowElement { | ||
44 | return { | ||
45 | type: 'mdxJsxFlowElement', | ||
46 | name: 'TabItem', | ||
47 | attributes: [ | ||
48 | { | ||
49 | type: 'mdxJsxAttribute', | ||
50 | name: 'value', | ||
51 | value, | ||
52 | }, | ||
53 | { | ||
54 | type: 'mdxJsxAttribute', | ||
55 | name: 'label', | ||
56 | value: label, | ||
57 | }, | ||
58 | ], | ||
59 | children: [ | ||
60 | { | ||
61 | type: node.type, | ||
62 | lang: node.lang, | ||
63 | value: code, | ||
64 | }, | ||
65 | ], | ||
66 | }; | ||
67 | } | ||
68 | |||
69 | function transformNode(node: Code): MdxJsxFlowElement[] { | ||
70 | const posixCode = node.value; | ||
71 | const windowsCode = posixCode.replaceAll(/(?<=^\w*)\.\//gm, '.\\'); | ||
72 | return [ | ||
73 | { | ||
74 | type: 'mdxJsxFlowElement', | ||
75 | name: 'Tabs', | ||
76 | attributes: [ | ||
77 | { | ||
78 | type: 'mdxJsxAttribute', | ||
79 | name: 'groupId', | ||
80 | value: 'posix2windows', | ||
81 | }, | ||
82 | ], | ||
83 | children: [ | ||
84 | createTabItem(posixCode, node, 'posix', 'Linux or macOS'), | ||
85 | createTabItem(windowsCode, node, 'windows', 'Windows (PowerShell)'), | ||
86 | ], | ||
87 | }, | ||
88 | ]; | ||
89 | } | ||
90 | |||
91 | function createImportNode(): MdxjsEsm { | ||
92 | return { | ||
93 | type: 'mdxjsEsm', | ||
94 | value: | ||
95 | "import Tabs from '@theme/Tabs'\nimport TabItem from '@theme/TabItem'", | ||
96 | data: { | ||
97 | estree: { | ||
98 | type: 'Program', | ||
99 | body: [ | ||
100 | { | ||
101 | type: 'ImportDeclaration', | ||
102 | specifiers: [ | ||
103 | { | ||
104 | type: 'ImportDefaultSpecifier', | ||
105 | local: { type: 'Identifier', name: 'Tabs' }, | ||
106 | }, | ||
107 | ], | ||
108 | source: { | ||
109 | type: 'Literal', | ||
110 | value: '@theme/Tabs', | ||
111 | raw: "'@theme/Tabs'", | ||
112 | }, | ||
113 | }, | ||
114 | { | ||
115 | type: 'ImportDeclaration', | ||
116 | specifiers: [ | ||
117 | { | ||
118 | type: 'ImportDefaultSpecifier', | ||
119 | local: { type: 'Identifier', name: 'TabItem' }, | ||
120 | }, | ||
121 | ], | ||
122 | source: { | ||
123 | type: 'Literal', | ||
124 | value: '@theme/TabItem', | ||
125 | raw: "'@theme/TabItem'", | ||
126 | }, | ||
127 | }, | ||
128 | ], | ||
129 | sourceType: 'module', | ||
130 | }, | ||
131 | }, | ||
132 | }; | ||
133 | } | ||
134 | |||
135 | export default function remarkPosix2Windows(): Transformer { | ||
136 | return (root) => { | ||
137 | let transformed = false; | ||
138 | let alreadyImported = false; | ||
139 | visit(root, (node) => { | ||
140 | if (isTabImport(node)) { | ||
141 | alreadyImported = true; | ||
142 | } | ||
143 | if (isParent(node)) { | ||
144 | let index = 0; | ||
145 | while (index < node.children.length) { | ||
146 | const child = node.children[index]; | ||
147 | if (child !== undefined && isPosix2Windows(child)) { | ||
148 | const result = transformNode(child); | ||
149 | node.children.splice(index, 1, ...result); | ||
150 | index += result.length; | ||
151 | transformed = true; | ||
152 | } else { | ||
153 | index += 1; | ||
154 | } | ||
155 | } | ||
156 | } | ||
157 | }); | ||
158 | if (transformed && !alreadyImported) { | ||
159 | if (isParent(root)) { | ||
160 | root.children.unshift(createImportNode()); | ||
161 | } else { | ||
162 | throw new Error("Cannot import '@theme/Tabs'"); | ||
163 | } | ||
164 | } | ||
165 | }; | ||
166 | } | ||