├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .husky
├── .gitignore
└── pre-commit
├── .lintstagedrc
├── .npmignore
├── .prettierignore
├── .prettierrc
├── .vercel
└── ignore-gh-pages.sh
├── LICENSE
├── README.md
├── core
├── README.md
├── package.json
├── src
│ ├── conf.ts
│ ├── directives.ts
│ ├── index.tsx
│ └── suggestions.ts
└── tsconfig.json
├── lerna.json
├── package.json
├── renovate.json
├── sandbox.config.json
├── tsconfig.json
└── website
├── .kktrc.ts
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── App.css
├── App.tsx
├── Header.module.less
├── Header.tsx
├── LoadFile.module.less
├── LoadFile.tsx
├── github.svg
├── index.tsx
├── logo.svg
├── nginx.conf.ts
├── nginx.svg
└── react-app-env.d.ts
└── tsconfig.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: jaywcjlove
2 | buy_me_a_coffee: jaywcjlove
3 | custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"]
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: github-actions
4 | directory: /
5 | schedule:
6 | interval: daily
7 |
8 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file
9 | # Enable version updates for npm
10 | - package-ecosystem: 'npm'
11 | # Look for `package.json` and `lock` files in the `root` directory
12 | directory: '/'
13 | # Check the npm registry for updates every day (weekdays)
14 | schedule:
15 | interval: 'daily'
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | push:
4 | branches:
5 | - main
6 |
7 | jobs:
8 | build-deploy:
9 | runs-on: ubuntu-latest
10 | permissions:
11 | contents: write
12 | id-token: write
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: actions/setup-node@v4
16 | with:
17 | node-version: 20
18 | registry-url: 'https://registry.npmjs.org'
19 |
20 | - name: Setup Yarn
21 | uses: threeal/setup-yarn-action@v2.0.0
22 | with:
23 | cache: false
24 | version: 1.22.21
25 |
26 | - run: yarn install
27 | - run: npm run build
28 | - run: npm run doc
29 |
30 | - name: Generate Contributors Images
31 | uses: jaywcjlove/github-action-contributors@main
32 | with:
33 | filter-author: (renovate\[bot\]|renovate-bot|dependabot\[bot\])
34 | output: website/build/CONTRIBUTORS.svg
35 | avatarSize: 42
36 |
37 | - name: Create Tag
38 | id: create_tag
39 | uses: jaywcjlove/create-tag-action@main
40 | with:
41 | token: ${{ secrets.GITHUB_TOKEN }}
42 | package-path: ./core/package.json
43 |
44 | - name: get tag version
45 | id: tag_version
46 | uses: jaywcjlove/changelog-generator@main
47 |
48 | - name: Deploy
49 | uses: peaceiris/actions-gh-pages@v4
50 | with:
51 | commit_message: ${{steps.tag_version.outputs.tag}} ${{ github.event.head_commit.message }}
52 | github_token: ${{ secrets.GITHUB_TOKEN }}
53 | publish_dir: ./website/build
54 |
55 | - name: Generate Changelog
56 | id: changelog
57 | uses: jaywcjlove/changelog-generator@main
58 | with:
59 | head-ref: ${{steps.create_tag.outputs.version}}
60 | filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}'
61 |
62 | - name: Create Release
63 | uses: jaywcjlove/create-tag-action@main
64 | if: steps.create_tag.outputs.successful
65 | with:
66 | package-path: ./package.json
67 | version: ${{steps.create_tag.outputs.version}}
68 | release: true
69 | prerelease: false
70 | draft: false
71 | body: |
72 | [](https://jaywcjlove.github.io/#/sponsor) [](https://uiwjs.github.io/npm-unpkg/#/pkg/monaco-editor-nginx@${{steps.create_tag.outputs.versionNumber}}/file/README.md)
73 |
74 | Documentation ${{ steps.changelog.outputs.tag }}: https://raw.githack.com/jaywcjlove/nginx-editor/${{ steps.changelog.outputs.gh-pages-short-hash }}/index.html
75 | Comparing Changes: ${{ steps.changelog.outputs.compareurl }}
76 |
77 | ```bash
78 | npm i monaco-editor-nginx@${{steps.create_tag.outputs.versionNumber}}
79 | ```
80 |
81 | ${{ steps.changelog.outputs.changelog }}
82 |
83 | - run: npm publish --access public --provenance
84 | name: 📦 monaco-editor-nginx publish to NPM
85 | continue-on-error: true
86 | working-directory: core
87 | env:
88 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
89 |
90 | # - run: npm run get:nginx
91 |
92 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | cjs
2 | esm
3 | build
4 | node_modules
5 | npm-debug.log*
6 | package-lock.json
7 | yarn.lock
8 |
9 | .eslintcache
10 | .DS_Store
11 | .cache
12 | .vscode
13 |
14 | *.bak
15 | *.tem
16 | *.temp
17 | #.swp
18 | *.*~
19 | ~*.*
20 |
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | ./node_modules/.bin/lint-staged --config .lintstagedrc
--------------------------------------------------------------------------------
/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.{js,jsx,tsx,ts,less,md,json}": [
3 | "pretty-quick --staged"
4 | ]
5 | }
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | tsconfig.json
3 | react-app-env.d.ts
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.md
2 | **/*.svg
3 | **/*.ejs
4 | **/*.yml
5 | package.json
6 | node_modules
7 | dist
8 | build
9 | lib
10 | test
11 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 120,
5 | "overrides": [
6 | {
7 | "files": ".prettierrc",
8 | "options": { "parser": "json" }
9 | },
10 | {
11 | "files": "*.{js,jsx}",
12 | "options": { "parser": "babel" }
13 | },
14 | {
15 | "files": "*.{ts,tsx}",
16 | "options": { "parser": "babel-ts" }
17 | },
18 | {
19 | "files": "*.{ts,tsx}",
20 | "options": { "parser": "typescript" }
21 | },
22 | {
23 | "files": "*.{less,css}",
24 | "options": { "parser": "css" }
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/.vercel/ignore-gh-pages.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Don't try to build the gh-pages branch.
4 |
5 | if [[ "$BRANCH" != "gh-pages" ]] ; then
6 | # Proceed with the build
7 | exit 1;
8 | else
9 | # Don't build
10 | exit 0;
11 | fi
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 小弟调调™
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | core/README.md
--------------------------------------------------------------------------------
/core/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Nginx language plugin for the [Monaco Editor](https://github.com/microsoft/monaco-editor). It provides the following features when editing [Nginx](https://nginx.org/) config files:
32 |
33 | - Syntax highlighting
34 |
35 | ## Quick Start
36 |
37 | ```bash
38 | npm install monaco-editor-nginx
39 | ```
40 |
41 | [](https://codesandbox.io/s/github/jaywcjlove/nginx-editor)
42 | [](https://jaywcjlove.github.io/nginx-editor/)
43 |
44 | ```jsx
45 | import MonacoEditor from '@uiw/react-monacoeditor';
46 | import 'monaco-editor-nginx';
47 |
48 | {
52 | setContentDownload(value);
53 | }}
54 | language="nginx"
55 | value={content}
56 | height="calc(100vh - 36px)"
57 | />
58 | ```
59 |
60 | or, Integrating the ESM version of the Monaco Editor
61 |
62 | ```js
63 | import * as monaco from 'monaco-editor';
64 | import 'monaco-editor-nginx';
65 |
66 | monaco.editor.create(document.getElementById("container"), {
67 | theme: 'nginx-theme',
68 | value: 'nginx code.....',
69 | language: 'nginx'
70 | });
71 | ```
72 |
73 | ## Development
74 |
75 | > [!WARNING]
76 | >
77 | > Using `npm` to install now cannot preview the example effect, but using `yarn@1` works. Currently, we are not sure what the reason is. If anyone knows, please let me know. Thanks a lot 🙏
78 |
79 | ```bash
80 | yarn install # ⚠️
81 | ```
82 |
83 | Runs the project in development mode.
84 |
85 | ```bash
86 | # Step 1, run first, listen to the component compile and output the .js file
87 | # listen for compilation output type .d.ts file
88 | npm run watch
89 | # Step 2, development mode, listen to compile preview website instance
90 | npm run start
91 | ```
92 |
93 | Builds the app for production to the build folder.
94 |
95 | ```bash
96 | npm run build
97 | ```
98 |
99 | The build is minified and the filenames include the hashes.
100 | Your app is ready to be deployed!
101 |
102 |
103 | ### Related
104 |
105 | - [@uiw/react-monacoeditor](https://github.com/jaywcjlove/react-monacoeditor): Monaco Editor component for React.
106 | - [@uiw/react-codemirror](https://github.com/uiwjs/react-codemirror): CodeMirror component for React. @codemirror
107 | - [@uiw/react-markdown-editor](https://github.com/uiwjs/react-markdown-editor): A markdown editor with preview, implemented with React.js and TypeScript.
108 | - [@uiw/react-md-editor](https://github.com/uiwjs/react-md-editor): A simple markdown editor with preview, implemented with React.js and TypeScript.
109 | - [@uiw/react-markdown-preview](https://github.com/jaywcjlove/react-monacoeditor): React component preview markdown text in web browser.
110 |
111 | ## Contributors
112 |
113 | As always, thanks to our amazing contributors!
114 |
115 |
116 |
117 |
118 |
119 | Made with [github-action-contributors](https://github.com/jaywcjlove/github-action-contributors).
120 | ## License
121 |
122 | Licensed under the MIT License.
123 |
--------------------------------------------------------------------------------
/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "monaco-editor-nginx",
3 | "version": "2.0.2",
4 | "description": "Nginx language for Monaco Editor.",
5 | "main": "cjs/index.js",
6 | "module": "esm/index.js",
7 | "homepage": "https://jaywcjlove.github.io/nginx-editor",
8 | "funding": "https://jaywcjlove.github.io/#/sponsor",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/jaywcjlove/nginx-editor.git"
12 | },
13 | "author": "",
14 | "license": "MIT",
15 | "peerDependencies": {
16 | "@babel/runtime": ">=7.10.0",
17 | "@nginx/reference-lib": ">=1.1.0",
18 | "monaco-editor": ">=0.22.3",
19 | "react": ">=16.9.0",
20 | "react-dom": ">=16.9.0"
21 | },
22 | "dependencies": {
23 | "@babel/runtime": "^7.18.6",
24 | "@nginx/reference-lib": "^1.1.0"
25 | },
26 | "devDependencies": {
27 | "monaco-editor": "^0.44.0"
28 | },
29 | "files": [
30 | "cjs",
31 | "esm",
32 | "src"
33 | ],
34 | "keywords": [
35 | "monaco-editor",
36 | "monaco-nginx",
37 | "nginx",
38 | "monaco",
39 | "editor",
40 | "editor-nginx",
41 | "monaco-editor-nginx"
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/core/src/conf.ts:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor';
2 |
3 | export const tokenConf: monaco.languages.IMonarchLanguage = {
4 | defaultToken: 'source',
5 | ignoreCase: true,
6 | brackets: [{ open: '{', close: '}', token: 'delimiter.bracket' }],
7 |
8 | tokenizer: {
9 | root: [
10 | [/(")/, 'delimiter.bracket'],
11 | // [/[{}()[\]]/, "@brackets"],
12 | [/[;,.]/, 'delimiter'],
13 | [/\\.* |~|~\*|!~|!~\*/, 'string.regexp'],
14 | [/\b\d+\w+\b/, 'number'],
15 | [/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?\b/, 'number'],
16 | [/\b(ip_hash|upstream|server)\b/, 'http.upstream'],
17 | [/\b(add_header|expires|server_tokens)\b/, 'http.headers'],
18 | [/\b(map|map_hash_max_size|map_hash_bucket_size)\b/, 'module.http'],
19 | [/\b(http)\b/, 'module.http'],
20 | [
21 | /\b(gzip|gzip_buffers|gzip_comp_level|gzip_disable|gzip_http.version|gzip_min_length|gzip_proxied|gzip_types|gzip_vary)\b/,
22 | 'module.http',
23 | ],
24 | [/\s(on|off)\b/, 'module.main'],
25 | [/\b(access_log|log_format)\s/, 'module.log'],
26 | [
27 | /\b(proxy_buffer_size|proxy_buffering|proxy_buffers|proxy_busy_buffers_size|proxy_cache|proxy_cache_background_update|proxy_cache_bypass|proxy_cache_convert_head|proxy_cache_key|proxy_cache_lock|proxy_cache_lock_age|proxy_cache_lock_timeout|proxy_cache_max_range_offset|proxy_cache_methods|proxy_cache_min_uses|proxy_cache_path|proxy_cache_purge|proxy_cache_revalidate|proxy_cache_use_stale|proxy_cache_valid|proxy_connect_timeout|proxy_headers_hash_bucket_size|proxy_headers_hash_max_size|proxy_hide_header|proxy_http_version|proxy_ignore_client_abort|proxy_intercept_errors|proxy_max_temp_file_size|proxy_method|proxy_next_upstream|proxy_next_upstream_tries|proxy_next_upstream_timeout|proxy_pass|proxy_pass_header|proxy_pass_request_body|proxy_pass_request_headers|proxy_read_timeout|proxy_redirect|proxy_redirect_errors|proxy_send_lowat|proxy_send_timeout|proxy_set_body|proxy_set_header|proxy_store|proxy_store_access|proxy_temp_file_write_size|proxy_t|emp_pathproxy_upstream_fail_timeout|proxy_upstream_max_fails)\b/,
28 | 'http.proxy',
29 | ],
30 | [
31 | /\b(ssl|ssl_buffer_size|ssl_certificate|ssl_certificate_key|ssl_ciphers|ssl_client_certificate|ssl_crl|ssl_dhparam|ssl_ecdh_curve|ssl_password_file|ssl_prefer_server_ciphers|ssl_protocols|ssl_session_cache|ssl_session_ticket_key|ssl_session_tickets|ssl_session_timeout|ssl_stapling|ssl_stapling_file|ssl_stapling_responder|ssl_stapling_verify|ssl_trusted_certificate|ssl_verify_client|ssl_verify_depth)\b/,
32 | 'module.http',
33 | ],
34 | [
35 | /\b(daemon|env|debug_points|error_log|log_not_found|include|lock_file|master_process|pid|ssl_engine|timer_resolution|types_hash_max_size|user|worker_cpu_affinity|worker_priority|worker_processes|worker_rlimit_core|worker_rlimit_nofile|worker_rlimit_sigpending|working_directory|try_files)\b/,
36 | 'module.main',
37 | ],
38 | [
39 | /\b(index|alias|chunked_transfer_encoding|client_body_in_file_only|client_body_buffer_size|client_body_temp_path|client_body_timeout|client_header_buffer_size|client_header_timeout|client_max_body_size|default_type|error_page|index |internal|keepalive_timeout|keepalive_requests|large_client_header_buffers|limit_except|limit_rate|listen|location|msie_padding|msie_refresh|optimize_server_names|port_in_redirect|recursive_error_pages|reset_timedout_connection|resolver|resolver_timeout|root|satisfy_any|send_timeout|sendfile|server|server_name|server_names_hash_max_size|server_names_hash_bucket_size|tcp_nodelay|tcp_nopush|types |try_files)\s/,
40 | 'module.http',
41 | ],
42 | [
43 | /\b(accept_mutex|accept_mutex_delay|debug_connection|devpoll_changes|devpoll_events|epoll_events|kqueue_changes|kqueue_events|multi_accept|rtsig_signo|rtsig_overflow_events|rtsig_overflow_test|rtsig_overflow_threshold|use|worker_connections)\b/,
44 | 'module.events',
45 | ],
46 | [/\b(add_before_body|add_after_body|addition_types)\b/, 'module.http.addition'],
47 | [/\b(events)\b/, 'module.events'],
48 | [
49 | /\b(fastcgi_index|fastcgi_hide_header|fastcgi_ignore_client_abort|fastcgi_intercept_errors|fastcgi_param|fastcgi_pass|fastcgi_pass_header|fastcgi_read_timeout|fastcgi_redirect_errors|fa|stcgi_storefastcgi_store_access|fastcgi_buffers|fastcgi_buffers_size|fastcgi_temp_path|fastcgi_buffer_size|fastcgi_connect_timeout|fastcgi_send_timeout|fastcgi_split_path_info)\b/,
50 | 'module.http',
51 | ],
52 | [/\$\w+/, 'variable'],
53 | [/#.*$/, 'comment'],
54 | // { include: "@numbers" },
55 | ],
56 | comment: [[/#.*$/, 'comment']],
57 | // urldeclaration: [
58 | // ['[^)\r\n]+', 'string'],
59 | // ['\\)', { token: 'delimiter.parenthesis', next: '@pop' }]
60 | // ],
61 | numbers: [
62 | ['-?(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?', { token: 'attribute.value.number', next: '@units' }],
63 | ['#[0-9a-fA-F_]+(?!\\w)', 'attribute.value.hex'],
64 | ],
65 | units: [['(M)?', 'attribute.value.unit', '@pop']],
66 | },
67 | };
68 |
69 | export const themeConfig: monaco.editor.IStandaloneThemeData = {
70 | colors: {
71 | // 'attribute.value.unit': '#68217a'
72 | },
73 | // base: 'vs-dark',
74 | base: 'vs',
75 | inherit: true,
76 | rules: [
77 | {
78 | token: 'module.http',
79 | foreground: '#007c7c',
80 | },
81 | {
82 | token: 'module.events',
83 | foreground: '#007c7c',
84 | },
85 | {
86 | token: 'http.headers',
87 | foreground: '#5400d7',
88 | },
89 | {
90 | token: 'http.proxy',
91 | foreground: '#00a039',
92 | },
93 | {
94 | token: 'module.main',
95 | foreground: '#c408ff',
96 | fontStyle: 'bold',
97 | },
98 | {
99 | token: 'module.log',
100 | foreground: '#4d6aab',
101 | },
102 | {
103 | token: 'module.http.addition',
104 | foreground: '#c408ff',
105 | },
106 | {
107 | token: 'keywords',
108 | foreground: '#9effff',
109 | fontStyle: 'bold',
110 | },
111 | {
112 | token: 'http.upstream',
113 | foreground: '#0078d0',
114 | fontStyle: 'bold',
115 | },
116 | {
117 | token: 'identifier',
118 | foreground: '#8e44ad',
119 | },
120 | {
121 | token: 'delimiter.bracket',
122 | foreground: '#737373',
123 | },
124 | {
125 | token: 'delimiter',
126 | foreground: '#737373',
127 | },
128 | {
129 | token: 'comment',
130 | foreground: '#b6b6b6',
131 | },
132 | ],
133 | };
134 |
135 | export const themeDarkConfig: monaco.editor.IStandaloneThemeData = {
136 | colors: {
137 | // 'attribute.value.unit': '#68217a'
138 | },
139 | // base: 'vs-dark',
140 | base: 'vs-dark',
141 | inherit: true,
142 | rules: [
143 | {
144 | token: 'module.http',
145 | foreground: '#00bbbb',
146 | },
147 | {
148 | token: 'module.events',
149 | foreground: '#00bbbb',
150 | },
151 | {
152 | token: 'http.headers',
153 | foreground: '#00bbbb',
154 | },
155 | {
156 | token: 'http.proxy',
157 | foreground: '#58f18e',
158 | },
159 | {
160 | token: 'module.main',
161 | foreground: '#c152e4',
162 | fontStyle: 'bold',
163 | },
164 | {
165 | token: 'module.log',
166 | foreground: '#4d6aab',
167 | },
168 | {
169 | token: 'module.http.addition',
170 | foreground: '#c152e4',
171 | },
172 | {
173 | token: 'keywords',
174 | foreground: '#9effff',
175 | fontStyle: 'bold',
176 | },
177 | {
178 | token: 'http.upstream',
179 | foreground: '#0078d0',
180 | fontStyle: 'bold',
181 | },
182 | {
183 | token: 'identifier',
184 | foreground: '#8e44ad',
185 | },
186 | {
187 | token: 'delimiter.bracket',
188 | foreground: '#737373',
189 | },
190 | {
191 | token: 'delimiter',
192 | foreground: '#737373',
193 | },
194 | ],
195 | };
196 |
--------------------------------------------------------------------------------
/core/src/directives.ts:
--------------------------------------------------------------------------------
1 | import { getDirectives, Format, getVariables, Directive } from '@nginx/reference-lib';
2 |
3 | export type Autocomplete = {
4 | /** name of the NGINX module */
5 | m: string;
6 | /** name */
7 | n: string;
8 | /** markdown-formatted description */
9 | d: string;
10 | /** default value, as an unformatted string as if a human typed it into an
11 | * nginx config */
12 | v?: string;
13 | /** markdown CSV for valid contexts */
14 | c?: string;
15 | /** markdown-formatted syntax specifications, including directive name.
16 | * Multiple syntaxes are seperated by newlines */
17 | s?: string;
18 | };
19 |
20 | function toAutocomplete(d: Directive): Autocomplete {
21 | const ret: Autocomplete = {
22 | m: d.module,
23 | n: d.name,
24 | d: d.description,
25 | c: d.contexts.map((c: string) => '`' + c + '`').join(', '),
26 | s: d.syntax.map((s: string) => `**${d.name}** ${s};`).join('\n'),
27 | };
28 |
29 | if (d.default) {
30 | ret.v = `${d.name} ${d.default};`;
31 | }
32 |
33 | return ret;
34 | }
35 |
36 | const variables = getVariables(Format.Markdown).map((v) => ({
37 | m: v.module,
38 | n: v.name,
39 | d: v.description,
40 | }));
41 |
42 | export const directives = getDirectives(Format.Markdown).map(toAutocomplete).concat(variables);
43 |
--------------------------------------------------------------------------------
/core/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor';
2 | import { themeConfig, themeDarkConfig, tokenConf } from './conf';
3 | import suggestions from './suggestions';
4 | import { directives } from './directives';
5 |
6 | // Register a new language
7 | monaco.languages.register({ id: 'nginx' });
8 | // monaco.languages.setLanguageConfiguration('nginx', {
9 | // autoClosingPairs: [
10 | // { open: '{', close: '}' },
11 | // { open: '"', close: '"' },
12 | // ],
13 | // });
14 | monaco.languages.setMonarchTokensProvider('nginx', tokenConf);
15 | monaco.editor.defineTheme('nginx-theme', themeConfig);
16 | monaco.editor.defineTheme('nginx-theme-dark', themeDarkConfig);
17 |
18 | monaco.languages.registerCompletionItemProvider('nginx', {
19 | provideCompletionItems: (model: monaco.editor.ITextModel, position: monaco.Position) => {
20 | const word = model.getWordUntilPosition(position);
21 | const range = {
22 | startLineNumber: position.lineNumber,
23 | endLineNumber: position.lineNumber,
24 | startColumn: word.startColumn,
25 | endColumn: word.endColumn,
26 | };
27 | return { suggestions: suggestions(range) };
28 | },
29 | });
30 |
31 | monaco.languages.registerHoverProvider('nginx', {
32 | provideHover: (model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken) => {
33 | const word = model.getWordAtPosition(position);
34 | if (!word) return;
35 | const data = directives.find((item) => item.n === word.word || item.n === `$${word.word}`);
36 | if (!data) return;
37 | const range = {
38 | startLineNumber: position.lineNumber,
39 | endLineNumber: position.lineNumber,
40 | startColumn: word.startColumn,
41 | endColumn: word.endColumn,
42 | };
43 | const contents = [{ value: `**\`${data.n}\`** | ${data.m} | ${data.c || ''}` }];
44 | if (data.s) {
45 | contents.push({ value: `**syntax:** ${data.s || ''}` });
46 | }
47 | if (data.v) {
48 | contents.push({ value: `**default:** ${data.v || ''}` });
49 | }
50 | if (data.d) {
51 | contents.push({ value: `${data.d}` });
52 | }
53 | return {
54 | contents: [...contents],
55 | range: range,
56 | };
57 | },
58 | });
59 |
--------------------------------------------------------------------------------
/core/src/suggestions.ts:
--------------------------------------------------------------------------------
1 | import * as monaco from 'monaco-editor';
2 | import { directives } from './directives';
3 |
4 | function getDirectives(range: monaco.IRange) {
5 | return directives.map((item) => ({
6 | label: item.n,
7 | kind: monaco.languages.CompletionItemKind.Keyword,
8 | insertText: item.n,
9 | documentation: item.d,
10 | range,
11 | }));
12 | }
13 |
14 | export default function suggestions(range: monaco.IRange): monaco.languages.CompletionList['suggestions'] {
15 | return [
16 | ...getDirectives(range),
17 | {
18 | label: 'upstream',
19 | kind: monaco.languages.CompletionItemKind.Snippet,
20 | insertText: [
21 | // eslint-disable-next-line no-template-curly-in-string
22 | 'upstream ${1:upstream_name} {',
23 | // eslint-disable-next-line no-template-curly-in-string
24 | '\tserver ${0:127.0.0.1:3110};',
25 | '}',
26 | ].join('\n'),
27 | insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
28 | detail: 'Upstream Example',
29 | range,
30 | },
31 | {
32 | label: 'proxy_pass',
33 | kind: monaco.languages.CompletionItemKind.Snippet,
34 | insertText: [
35 | // eslint-disable-next-line no-template-curly-in-string
36 | 'proxy_pass ${1:http}://${0192.168.188.222:32001};',
37 | ].join('\n'),
38 | insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
39 | detail: 'proxy_pass Example',
40 | range,
41 | },
42 | {
43 | label: 'location',
44 | kind: monaco.languages.CompletionItemKind.Snippet,
45 | insertText: [
46 | // eslint-disable-next-line no-template-curly-in-string
47 | 'location ${1:/} {\n\t${0}\n}',
48 | ].join('\n'),
49 | insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
50 | detail: 'proxy_pass Example',
51 | range,
52 | },
53 | ];
54 | }
55 |
--------------------------------------------------------------------------------
/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig",
3 | "compilerOptions": {
4 | "outDir": "../cjs",
5 | "baseUrl": "."
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.2",
3 | "packages": ["website", "core"]
4 | }
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "prepare": "npm run prettier",
5 | "⬇️⬇️⬇️⬇️⬇️ package ⬇️⬇️⬇️⬇️⬇️": "▼▼▼▼▼ package ▼▼▼▼▼",
6 | "build": "lerna exec --scope monaco-editor-nginx -- tsbb build src/*.tsx --use-babel --cjs cjs --bail",
7 | "watch": "lerna exec \"tsbb watch src/*.{tsx,ts} --use-babel --cjs cjs\" --scope monaco-editor-nginx",
8 | "⬆️⬆️⬆️⬆️⬆️ package ⬆️⬆️⬆️⬆️⬆️": "▲▲▲▲▲ package ▲▲▲▲▲",
9 | "start": "lerna exec --scope website -- npm run start",
10 | "doc": "lerna exec --scope website -- npm run build",
11 | "version": "lerna version --exact --force-publish --no-push --no-git-tag-version",
12 | "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"",
13 | "map": "source-map-explorer website/build/static/js/*.js --html website/build/website-result.html"
14 | },
15 | "devDependencies": {
16 | "@types/turndown": "~5.0.1",
17 | "cheerio": "~1.0.0-rc.10",
18 | "compile-less-cli": "~1.9.0",
19 | "husky": "^9.0.0",
20 | "lerna": "^8.0.0",
21 | "lint-staged": "^15.2.5",
22 | "prettier": "^3.3.1",
23 | "pretty-quick": "^4.0.0",
24 | "react": "~18.2.0",
25 | "react-dom": "~18.2.0",
26 | "source-map-explorer": "^2.5.3",
27 | "ts-node": "^10.9.1",
28 | "tsbb": "^4.1.11",
29 | "turndown": "^7.1.2",
30 | "typescript": "^5.1.3"
31 | },
32 | "workspaces": [
33 | "website",
34 | "core"
35 | ],
36 | "overrides": {
37 | "typescript": "^5.1.3"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["config:base"],
3 | "packageRules": [
4 | {
5 | "matchPackagePatterns": ["*"],
6 | "rangeStrategy": "replace"
7 | }
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/sandbox.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "template": "node"
3 | }
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "lib": ["dom", "dom.iterable", "es6"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "declaration": true,
16 | "baseUrl": ".",
17 | "jsx": "react-jsx",
18 | "noFallthroughCasesInSwitch": true
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/website/.kktrc.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import webpack from 'webpack';
3 | import { LoaderConfOptions, WebpackConfiguration } from 'kkt';
4 | import lessModules from '@kkt/less-modules';
5 | import rawModules from '@kkt/raw-modules';
6 | import scopePluginOptions from '@kkt/scope-plugin-options';
7 | import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
8 | import pkg from 'monaco-editor-nginx/package.json';
9 |
10 | export default (conf: WebpackConfiguration, env: 'production' | 'development', options: LoaderConfOptions) => {
11 | conf = rawModules(conf, env, { ...options });
12 | conf = scopePluginOptions(conf, env, {
13 | ...options,
14 | allowedFiles: [path.resolve(process.cwd(), 'README.md')],
15 | });
16 | conf = lessModules(conf, env, options);
17 | // Get the project version.
18 | conf.plugins!.push(
19 | new webpack.DefinePlugin({
20 | VERSION: JSON.stringify(pkg.version),
21 | }),
22 | );
23 | conf.plugins!.push(
24 | new MonacoWebpackPlugin({
25 | languages: ['json'],
26 | }),
27 | );
28 | if (env === 'production') {
29 | conf.output = { ...conf.output, publicPath: './' };
30 | conf.optimization = {
31 | ...conf.optimization,
32 | splitChunks: {
33 | automaticNameDelimiter: '.',
34 | maxSize: 500000,
35 | minSize: 100000,
36 | cacheGroups: {
37 | reactvendor: {
38 | test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
39 | name: 'react-vendor',
40 | reuseExistingChunk: true,
41 | chunks: 'all',
42 | priority: -10,
43 | },
44 | monacoeditor: {
45 | test: /[\\/]node_modules[\\/](monaco-editor)[\\/]/,
46 | name: 'monaco-editor-vendor',
47 | chunks: 'all',
48 | },
49 | },
50 | },
51 | };
52 | }
53 | return conf;
54 | };
55 |
--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "website",
3 | "version": "2.0.2",
4 | "private": true,
5 | "scripts": {
6 | "start": "kkt start",
7 | "build": "kkt build"
8 | },
9 | "license": "MIT",
10 | "dependencies": {
11 | "@wcj/dark-mode": "^1.0.13",
12 | "monaco-editor-nginx": "2.0.2",
13 | "react": "~18.2.0",
14 | "react-dom": "~18.2.0"
15 | },
16 | "devDependencies": {
17 | "@kkt/less-modules": "^7.5.4",
18 | "@kkt/raw-modules": "^7.5.4",
19 | "@kkt/scope-plugin-options": "^7.5.4",
20 | "@types/react": "^18.0.31",
21 | "@types/react-dom": "^18.0.11",
22 | "@uiw/react-monacoeditor": "^3.5.8",
23 | "kkt": "^7.5.4",
24 | "monaco-editor-webpack-plugin": "~7.1.0"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/website/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaywcjlove/nginx-editor/5ebc190a974cedb603526009e2be28f2f2995bcc/website/public/favicon.ico
--------------------------------------------------------------------------------
/website/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
20 | nginx online editor
21 |
22 |
23 |
24 |
25 |
26 |
36 |
37 |
--------------------------------------------------------------------------------
/website/src/App.css:
--------------------------------------------------------------------------------
1 | [data-color-mode*='dark'],
2 | [data-color-mode*='dark'] body {
3 | --color-theme-hover-text: #ffffff33;
4 | }
5 | [data-color-mode*='light'],
6 | [data-color-mode*='light'] body {
7 | --color-theme-hover-text: #c9d1d9;
8 | }
9 |
10 | body {
11 | margin: 0;
12 | padding: 0;
13 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
14 | 'Droid Sans', 'Helvetica Neue', sans-serif;
15 | -webkit-font-smoothing: antialiased;
16 | -moz-osx-font-smoothing: grayscale;
17 | }
18 |
19 | html,
20 | body {
21 | background-color: var(--color-theme-bg);
22 | color: var(--color-theme-text);
23 | }
24 |
25 | .App {
26 | overflow: hidden;
27 | }
28 |
--------------------------------------------------------------------------------
/website/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from 'react';
2 | import MonacoEditor, { RefEditorInstance } from '@uiw/react-monacoeditor';
3 | import { nginxStr } from './nginx.conf';
4 | import Header from './Header';
5 | import './App.css';
6 |
7 | import 'monaco-editor-nginx';
8 |
9 | const App: React.FC = () => {
10 | const [theme, setTheme] = useState(
11 | document.documentElement.dataset.colorMode === 'dark' || !document.documentElement.dataset.colorMode
12 | ? 'vs-dark'
13 | : 'vs',
14 | );
15 | const [content, setContent] = useState(nginxStr || '');
16 | const [contentDownload, setContentDownload] = useState(content || nginxStr || '');
17 | const editor = useRef(null);
18 | function resizeHandle(evn: UIEvent) {
19 | const { target } = evn;
20 | const width = (target as Window).innerWidth;
21 | const height = (target as Window).innerHeight;
22 | if (editor.current && editor.current.editor) {
23 | editor.current.editor.layout({ width, height: height - 36 });
24 | }
25 | }
26 | useEffect(() => {
27 | if (editor.current && window) {
28 | window.addEventListener('resize', resizeHandle, false);
29 | }
30 | setTheme(document.documentElement.dataset.colorMode === 'dark' ? 'vs-dark' : 'vs');
31 | document.addEventListener('colorschemechange', (e) => {
32 | setTheme(e.detail.colorScheme === 'dark' ? 'vs-dark' : 'vs');
33 | });
34 | return () => {
35 | window && window.removeEventListener('resize', resizeHandle, false);
36 | };
37 | }, []);
38 | return (
39 |
40 | {
43 | setContent(text);
44 | }}
45 | />
46 | {
50 | setContentDownload(value);
51 | }}
52 | language="nginx"
53 | value={content}
54 | height="calc(100vh - 36px)"
55 | />
56 |
57 | );
58 | };
59 |
60 | export default App;
61 |
--------------------------------------------------------------------------------
/website/src/Header.module.less:
--------------------------------------------------------------------------------
1 | .header {
2 | height: 32px;
3 | display: flex;
4 | padding: 0 10px;
5 | align-items: center;
6 | user-select: none;
7 | a {
8 | color: var(--color-theme-text);
9 | display: flex;
10 | align-items: center;
11 | }
12 | }
13 |
14 | .title {
15 | padding-left: 6px;
16 | }
17 |
18 | .filename {
19 | flex: 1;
20 | text-align: center;
21 | font-size: 14px;
22 | }
23 |
--------------------------------------------------------------------------------
/website/src/Header.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import '@wcj/dark-mode';
3 | import styles from 'Header.module.less';
4 | import LoadFile, { LoadFileProps } from './LoadFile';
5 | // @ts-ignore
6 | import { ReactComponent } from './github.svg';
7 | // @ts-ignore
8 | import { ReactComponent as NginxLogo } from './nginx.svg';
9 |
10 | type HeaderProps = LoadFileProps & {};
11 |
12 | export default function Header(props: HeaderProps) {
13 | const { onLoadContent } = props;
14 | const [filename, setFilename] = useState('nginx.example.conf');
15 | return (
16 |
17 |
18 |
nginx editor
19 |
{filename}
20 |
{
24 | setFilename(file!.name || '');
25 | onLoadContent && onLoadContent(text, evn);
26 | }}
27 | />
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/website/src/LoadFile.module.less:
--------------------------------------------------------------------------------
1 | .button {
2 | border: 0;
3 | background-color: transparent !important;
4 | color: var(--color-theme-text);
5 | padding: 2px 5px;
6 | transition: background-color 0.3s;
7 | cursor: pointer;
8 | &:focus {
9 | box-shadow: inset 0 0 0 #000;
10 | outline: 0;
11 | }
12 | &:hover {
13 | background-color: var(--color-theme-hover-text) !important;
14 | }
15 | + .button {
16 | margin-right: 3px;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/website/src/LoadFile.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect, Fragment } from 'react';
2 | import styles from './LoadFile.module.less';
3 |
4 | export type LoadFileProps = {
5 | content?: string;
6 | filename?: string;
7 | onLoadContent?: (text: string, evn: ProgressEvent, file?: File) => void;
8 | };
9 |
10 | export default function LoadFile(props: LoadFileProps) {
11 | const { onLoadContent } = props;
12 | const inputRef = useRef(null);
13 | const fileReader = useRef(new FileReader());
14 | const fileState = useRef();
15 | useEffect(() => {
16 | const inp = inputRef.current;
17 | if (inp) {
18 | fileReader.current.onload = (e) => {
19 | if (e.target && e.target.result) {
20 | onLoadContent && onLoadContent(e.target.result.toString(), e, fileState.current);
21 | }
22 | };
23 | inp.addEventListener('change', handleUpload, false);
24 | }
25 | return () => {
26 | if (inp) {
27 | inp.removeEventListener('change', handleUpload, false);
28 | }
29 | };
30 | // eslint-disable-next-line
31 | }, []);
32 |
33 | function handleUpload(eve: Event) {
34 | const { target } = eve as unknown as React.ChangeEvent;
35 | if (target.files && target.files[0]) {
36 | fileState.current = target.files[0];
37 | fileReader.current.readAsText(target.files[0]);
38 | }
39 | }
40 |
41 | function handleClick() {
42 | inputRef.current!.click();
43 | }
44 |
45 | function handleSaveAs() {
46 | let aTag = document.createElement('a');
47 | if ('download' in aTag) {
48 | aTag.setAttribute('download', props.filename || 'nginx.conf');
49 | let blob = new Blob([props.content || ''], { type: '' });
50 | aTag.setAttribute('href', URL.createObjectURL(blob));
51 | document.body.appendChild(aTag);
52 | const eventMouse = document.createEvent('MouseEvents');
53 | eventMouse.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
54 | aTag.dispatchEvent(eventMouse);
55 | document.body.removeChild(aTag);
56 | }
57 | }
58 |
59 | return (
60 |
61 | {props.content && (
62 |
65 | )}
66 |
70 |
71 | );
72 | }
73 |
--------------------------------------------------------------------------------
/website/src/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/website/src/index.tsx:
--------------------------------------------------------------------------------
1 | import App from './App';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | const container = document.getElementById('root');
5 | const root = createRoot(container!);
6 | root.render();
7 |
--------------------------------------------------------------------------------
/website/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/website/src/nginx.conf.ts:
--------------------------------------------------------------------------------
1 | export const nginxStr = `user www www; ## Default: nobody
2 | worker_processes 5; ## Default: 1
3 | error_log logs/error.log;
4 | pid logs/nginx.pid;
5 | worker_rlimit_nofile 8192;
6 |
7 | events {
8 | worker_connections 4096; ## Default: 1024
9 | }
10 |
11 | http {
12 | include conf/mime.types;
13 | include /etc/nginx/proxy.conf;
14 | include /etc/nginx/fastcgi.conf;
15 | index index.html index.htm index.php;
16 |
17 | default_type application/octet-stream;
18 | log_format main '$remote_addr - $remote_user [$time_local] $status '
19 | '"$request" $body_bytes_sent "$http_referer" '
20 | '"$http_user_agent" "$http_x_forwarded_for"';
21 | access_log logs/access.log main;
22 | sendfile on;
23 | tcp_nopush on;
24 | server_names_hash_bucket_size 128; # this seems to be required for some vhosts
25 |
26 | server { # php/fastcgi
27 | listen 80;
28 | server_name domain1.com www.domain1.com;
29 | access_log logs/domain1.access.log main;
30 | root html;
31 |
32 | location ~ \\.php$ {
33 | fastcgi_pass 127.0.0.1:1025;
34 | }
35 | }
36 |
37 | server { # simple reverse-proxy
38 | listen 80;
39 | server_name domain2.com www.domain2.com;
40 | access_log logs/domain2.access.log main;
41 |
42 | # serve static files
43 | location ~ ^/(images|javascript|js|css|flash|media|static)/ {
44 | root /var/www/virtual/big.server.com/htdocs;
45 | expires 30d;
46 | }
47 |
48 | # pass requests for dynamic content to rails/turbogears/zope, et al
49 | location / {
50 | proxy_pass http://127.0.0.1:8080;
51 | }
52 | }
53 |
54 | upstream big_server_com {
55 | server 127.0.0.3:8000 weight=5;
56 | server 127.0.0.3:8001 weight=5;
57 | server 192.168.0.1:8000;
58 | server 192.168.0.1:8001;
59 | }
60 |
61 | server { # simple load balancing
62 | listen 80;
63 | server_name big.server.com;
64 | access_log logs/big.server.access.log main;
65 |
66 | location / {
67 | proxy_pass http://big_server_com;
68 | }
69 | }
70 | }`;
71 |
--------------------------------------------------------------------------------
/website/src/nginx.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/website/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | //////
2 | ///
3 |
4 | declare module '*.module.less' {
5 | const classes: { readonly [key: string]: string };
6 | export default classes;
7 | }
8 |
9 | declare module '*.md' {
10 | const src: string;
11 | export default src;
12 | }
13 |
--------------------------------------------------------------------------------
/website/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig",
3 | "include": [".kktrc.ts", "src"],
4 | "compilerOptions": {
5 | "jsx": "react-jsx",
6 | "baseUrl": "./src",
7 | "noEmit": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------