├── .github
├── dependabot.yml
├── dockerhub.png
├── logo.png
├── preview.png
└── workflows
│ ├── codecov.yml
│ ├── release.yaml
│ └── scan.yml
├── .gitignore
├── .goreleaser.yaml
├── LICENSE
├── README.md
├── README_CN.md
├── SECURITY.md
├── docker
├── Dockerfile
└── Dockerfile.goreleaser
├── go.mod
├── go.sum
├── internal
├── checker
│ └── checker.go
├── cmd
│ └── cmd.go
├── define
│ └── define.go
├── formatter
│ ├── beautifier.go
│ ├── beautifier.js
│ ├── formatter.go
│ └── formatter_test.go
├── server
│ ├── assets.go
│ ├── assets
│ │ ├── base.css
│ │ ├── base.js
│ │ └── index.html
│ └── server.go
├── updater
│ ├── update_test.go
│ └── updater.go
└── version
│ └── version.go
└── main.go
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "gomod"
4 | directory: "/"
5 | schedule:
6 | interval: "weekly"
--------------------------------------------------------------------------------
/.github/dockerhub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/soulteary/nginx-formatter/8d01de13b95db0c83e1cb94526a742f06b707a42/.github/dockerhub.png
--------------------------------------------------------------------------------
/.github/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/soulteary/nginx-formatter/8d01de13b95db0c83e1cb94526a742f06b707a42/.github/logo.png
--------------------------------------------------------------------------------
/.github/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/soulteary/nginx-formatter/8d01de13b95db0c83e1cb94526a742f06b707a42/.github/preview.png
--------------------------------------------------------------------------------
/.github/workflows/codecov.yml:
--------------------------------------------------------------------------------
1 | name: Codecov
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 |
7 | jobs:
8 | CodeCov:
9 | runs-on: ubuntu-latest
10 |
11 | env:
12 | GO111MODULE: on
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v3
16 | with:
17 | fetch-depth: 0
18 |
19 | - name: Set up Go
20 | uses: actions/setup-go@v3
21 | with:
22 | go-version: ${{ env.GO_VERSION }}
23 |
24 | - name: Cache Go modules
25 | uses: actions/cache@v2
26 | with:
27 | path: ~/go/pkg/mod
28 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
29 | restore-keys: |
30 | ${{ runner.os }}-go-
31 |
32 | - name: Run coverage
33 | run: go test ./... -coverprofile=coverage.out -covermode=atomic
34 | - name: Upload coverage to Codecov
35 | uses: codecov/codecov-action@v3
36 | with:
37 | token: ${{ secrets.CODECOV_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - 'main'
8 | tags:
9 | - 'v*'
10 | env:
11 | GO_VERSION: "1.20"
12 |
13 | permissions:
14 | contents: write
15 | id-token: write
16 | packages: write
17 |
18 | jobs:
19 | build:
20 | runs-on: ubuntu-latest
21 | env:
22 | GO111MODULE: on
23 | DOCKER_CLI_EXPERIMENTAL: "enabled"
24 | steps:
25 | -
26 | name: Checkout
27 | uses: actions/checkout@v3
28 | with:
29 | fetch-depth: 0
30 | -
31 | name: Set up Go
32 | uses: actions/setup-go@v3
33 | with:
34 | go-version: ${{ env.GO_VERSION }}
35 | -
36 | name: Set up QEMU
37 | uses: docker/setup-qemu-action@v1
38 | -
39 | name: Cache Go modules
40 | uses: actions/cache@v1
41 | with:
42 | path: ~/go/pkg/mod
43 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
44 | restore-keys: |
45 | ${{ runner.os }}-go-
46 | -
47 | name: Tests
48 | run: |
49 | go mod tidy
50 | go test -v ./...
51 | - name: Login to Docker Hub
52 | if: github.event_name != 'pull_request'
53 | uses: docker/login-action@v2
54 | with:
55 | username: ${{ secrets.DOCKERHUB_USERNAME }}
56 | password: ${{ secrets.DOCKERHUB_TOKEN }}
57 | -
58 | name: Run GoReleaser
59 | uses: goreleaser/goreleaser-action@v3
60 | if: success() && startsWith(github.ref, 'refs/tags/')
61 | with:
62 | version: latest
63 | args: release --clean
64 | env:
65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/scan.yml:
--------------------------------------------------------------------------------
1 | name: "Security Scan"
2 |
3 | # Run workflow each time code is pushed to your repository and on a schedule.
4 | # The scheduled workflow runs every at 00:00 on Sunday UTC time.
5 | on:
6 | workflow_dispatch:
7 | push:
8 | branches:
9 | - main
10 | paths:
11 | - '*.go'
12 | pull_request:
13 | branches:
14 | - main
15 | schedule:
16 | - cron: '0 0 * * 0'
17 |
18 | jobs:
19 | scan:
20 | permissions: write-all
21 | runs-on: ubuntu-latest
22 | steps:
23 | - name: Check out code into the Go module directory
24 | uses: actions/checkout@v2
25 | - name: Security Scan
26 | uses: securego/gosec@master
27 | with:
28 | # we let the report trigger content trigger a failure using the GitHub Security features.
29 | args: '-no-fail -fmt sarif -out results.sarif ./...'
30 | - name: Upload SARIF file
31 | uses: github/codeql-action/upload-sarif@v2
32 | with:
33 | # Path to SARIF file relative to the root of the repository
34 | sarif_file: results.sarif
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | # vendor/
16 |
17 | *.conf
18 | .DS_Store
--------------------------------------------------------------------------------
/.goreleaser.yaml:
--------------------------------------------------------------------------------
1 | project_name: nginx-formatter
2 |
3 | builds:
4 | - <<: &build_defaults
5 | env:
6 | - CGO_ENABLED=0
7 | ldflags:
8 | - -X "github.com/soulteary/nginx-formatter/internal/version.Version={{ .Tag }}"
9 | id: macos
10 | goos: [ darwin ]
11 | goarch: [ amd64, arm64 ]
12 |
13 | - <<: *build_defaults
14 | id: linux
15 | goos: [linux]
16 | goarch: ["386", arm, amd64, arm64]
17 | goarm:
18 | - "7"
19 | - "6"
20 |
21 | dockers:
22 |
23 | - image_templates:
24 | - "soulteary/nginx-formatter:linux-amd64-{{ .Tag }}"
25 | - "soulteary/nginx-formatter:linux-amd64"
26 | dockerfile: docker/Dockerfile.goreleaser
27 | use: buildx
28 | goarch: amd64
29 | build_flag_templates:
30 | - "--pull"
31 | - "--platform=linux/amd64"
32 | - "--label=org.opencontainers.image.title={{ .ProjectName }}"
33 | - "--label=org.opencontainers.image.description={{ .ProjectName }}"
34 | - "--label=org.opencontainers.image.url=https://github.com/soulteary/nginx-formatter"
35 | - "--label=org.opencontainers.image.source=https://github.com/soulteary/nginx-formatter"
36 | - "--label=org.opencontainers.image.version={{ .Version }}"
37 | - "--label=org.opencontainers.image.created={{ .Date }}"
38 | - "--label=org.opencontainers.image.revision={{ .FullCommit }}"
39 | - "--label=org.opencontainers.image.licenses=Apache-v2"
40 |
41 | - image_templates:
42 | - "soulteary/nginx-formatter:linux-arm64-{{ .Tag }}"
43 | - "soulteary/nginx-formatter:linux-arm64"
44 | dockerfile: docker/Dockerfile.goreleaser
45 | use: buildx
46 | goos: linux
47 | goarch: arm64
48 | goarm: ''
49 | build_flag_templates:
50 | - "--pull"
51 | - "--platform=linux/arm64"
52 | - "--label=org.opencontainers.image.title={{ .ProjectName }}"
53 | - "--label=org.opencontainers.image.description={{ .ProjectName }}"
54 | - "--label=org.opencontainers.image.url=https://github.com/soulteary/nginx-formatter"
55 | - "--label=org.opencontainers.image.source=https://github.com/soulteary/nginx-formatter"
56 | - "--label=org.opencontainers.image.version={{ .Version }}"
57 | - "--label=org.opencontainers.image.created={{ .Date }}"
58 | - "--label=org.opencontainers.image.revision={{ .FullCommit }}"
59 | - "--label=org.opencontainers.image.licenses=Apache-v2"
60 |
61 | - image_templates:
62 | - "soulteary/nginx-formatter:linux-armv7-{{ .Tag }}"
63 | - "soulteary/nginx-formatter:linux-armv7"
64 | dockerfile: docker/Dockerfile.goreleaser
65 | use: buildx
66 | goos: linux
67 | goarch: arm
68 | goarm: "7"
69 | build_flag_templates:
70 | - "--pull"
71 | - "--platform=linux/arm/v7"
72 | - "--label=org.opencontainers.image.title={{ .ProjectName }}"
73 | - "--label=org.opencontainers.image.description={{ .ProjectName }}"
74 | - "--label=org.opencontainers.image.url=https://github.com/soulteary/nginx-formatter"
75 | - "--label=org.opencontainers.image.source=https://github.com/soulteary/nginx-formatter"
76 | - "--label=org.opencontainers.image.version={{ .Version }}"
77 | - "--label=org.opencontainers.image.created={{ .Date }}"
78 | - "--label=org.opencontainers.image.revision={{ .FullCommit }}"
79 | - "--label=org.opencontainers.image.licenses=Apache-v2"
80 |
81 | - image_templates:
82 | - "soulteary/nginx-formatter:linux-armv6-{{ .Tag }}"
83 | - "soulteary/nginx-formatter:linux-armv6"
84 | dockerfile: docker/Dockerfile.goreleaser
85 | use: buildx
86 | goos: linux
87 | goarch: arm
88 | goarm: "6"
89 | build_flag_templates:
90 | - "--pull"
91 | - "--platform=linux/arm/v6"
92 | - "--label=org.opencontainers.image.title={{ .ProjectName }}"
93 | - "--label=org.opencontainers.image.description={{ .ProjectName }}"
94 | - "--label=org.opencontainers.image.url=https://github.com/soulteary/nginx-formatter"
95 | - "--label=org.opencontainers.image.source=https://github.com/soulteary/nginx-formatter"
96 | - "--label=org.opencontainers.image.version={{ .Version }}"
97 | - "--label=org.opencontainers.image.created={{ .Date }}"
98 | - "--label=org.opencontainers.image.revision={{ .FullCommit }}"
99 | - "--label=org.opencontainers.image.licenses=Apache-v2"
100 |
101 |
102 | docker_manifests:
103 | - name_template: "soulteary/nginx-formatter:{{ .Tag }}"
104 | image_templates:
105 | - "soulteary/nginx-formatter:linux-amd64-{{ .Tag }}"
106 | - "soulteary/nginx-formatter:linux-arm64-{{ .Tag }}"
107 | - "soulteary/nginx-formatter:linux-armv7-{{ .Tag }}"
108 | - "soulteary/nginx-formatter:linux-armv6-{{ .Tag }}"
109 | skip_push: "false"
110 |
111 | - name_template: "soulteary/nginx-formatter:latest"
112 | image_templates:
113 | - "soulteary/nginx-formatter:linux-amd64-{{ .Tag }}"
114 | - "soulteary/nginx-formatter:linux-arm64-{{ .Tag }}"
115 | - "soulteary/nginx-formatter:linux-armv7-{{ .Tag }}"
116 | - "soulteary/nginx-formatter:linux-armv6-{{ .Tag }}"
117 | skip_push: "false"
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nginx Formatter
2 |
3 | [](https://github.com/soulteary/nginx-formatter/actions/workflows/github-code-scanning/codeql) [](https://github.com/soulteary/nginx-formatter/actions/workflows/codecov.yml) [](https://github.com/soulteary/nginx-formatter/actions/workflows/scan.yml) [](https://github.com/soulteary/nginx-formatter/actions/workflows/release.yaml)  [](https://hub.docker.com/r/soulteary/nginx-formatter)
4 |
5 |
6 | ENGLISH | 中文文档
7 |
8 |
9 |
10 |
11 | Nginx configuration formatter ~10MB size, support CLI, WebUI, x86, ARM, Linux, macOS.
12 |
13 |
14 |
15 | ## Download
16 |
17 | Download the binaries for your system and architecture from the [releases page](https://github.com/soulteary/nginx-formatter/releases).
18 |
19 |
20 |
21 | If you use docker, you can use the following command ([DockerHub](https://hub.docker.com/r/soulteary/nginx-formatter)):
22 |
23 | ```bash
24 | docker pull soulteary/nginx-formatter:latest
25 | docker pull soulteary/nginx-formatter:v1.1.1
26 | ```
27 |
28 | ## Usage
29 |
30 | Use default parameters to format all configuration files in the current directory:
31 |
32 | ```bash
33 | ./nginx-formatter
34 | ```
35 |
36 | ### Common Usage (CLI & WebUI)
37 |
38 | Use different indentation symbols (You can use spaces, tabs, ` `, `\s`, `\t`) and indentation amounts:
39 |
40 | ```bash
41 | ./nginx-formatter -indent=4 -char=" "
42 | ```
43 |
44 | ### CLI Usage
45 |
46 | Format the configuration file in the specified directory:
47 |
48 | ```bash
49 | ./nginx-formatter -input=./your-dir-path
50 | ```
51 |
52 | Format a file somewhere and save it in a new directory:
53 |
54 | ```bash
55 | ./nginx-formatter -input=./your-dir-path -output=./your-output-dir
56 | ```
57 |
58 | ### WebUI Usage
59 |
60 | Start the web interface:
61 |
62 | ```bash
63 | ./nginx-formatter -web
64 | ```
65 |
66 | specified the port:
67 |
68 | ```bash
69 | ./nginx-formatter -web -port=8123
70 | ```
71 |
72 | ### Docker Usage
73 |
74 | There is no difference between using parameters in Docker and the above, for example, we start a Web UI formatting tool service in Docker:
75 |
76 | ```bash
77 | docker run --rm -it -p 8080:8080 soulteary/nginx-formatter:v1.1.1 -web
78 | ```
79 |
80 | If you want to format the configuration of the current directory, you can use the program in Docker with a command similar to the following:
81 |
82 | ```bash
83 | docker run --rm -it -v `pwd`:/app soulteary/nginx-formatter:v1.1.1 -input=/app
84 | ```
85 |
86 | ## Full parameters supported
87 |
88 | List of parameters supported:
89 |
90 | ```bash
91 | Nginx Formatter
92 |
93 | Usage of ./nginx-formatter:
94 | -char
95 | Indent char, defualt: (default " ")
96 | -indent int
97 | Indent size, defualt: 2 (default 2)
98 | -input string
99 | Input directory
100 | -output string
101 | Output directory
102 | -port 8080
103 | WebUI Port, defualt: 8080 (default 8080)
104 | -web false
105 | Enable WebUI, defualt: false
106 | ```
107 |
108 | ## Credits
109 |
110 | Formatter Components
111 |
112 | - Slomkowski Created a beautifier for nginx config files with Python under [Apache-2.0 license], 24/06/2016
113 | - https://github.com/1connect/nginx-config-formatter (https://github.com/slomkowski/nginx-config-formatter)
114 | - Yosef Ported the JavaScript beautifier under [Apache-2.0 license], 24/08/2016
115 | - https://github.com/vasilevich/nginxbeautifier
116 | - soulteary Modify the JavaScript version for golang execution, under [Apache-2.0 license], 18/04/2023:
117 | - simplify the program, fix bugs, improve running speed, and allow running in golang
118 | - https://github.com/soulteary/nginx-formatter
119 |
120 | Runtime dependent Components
121 |
122 | - ECMAScript 5.1(+) implementation in Go, under [MIT license].
123 | - https://github.com/dop251/goja
124 |
125 | Web Components
126 |
127 | - Gin is a HTTP web framework written in Go (Golang), under [MIT license].
128 | - https://github.com/gin-gonic/gin
129 | - Code Mirror, in-browser code editor, under [MIT license].
130 | - https://github.com/codemirror/codemirror5
131 |
--------------------------------------------------------------------------------
/README_CN.md:
--------------------------------------------------------------------------------
1 | # Nginx Formatter / Nginx 格式化工具
2 |
3 | [](https://github.com/soulteary/nginx-formatter/actions/workflows/github-code-scanning/codeql) [](https://github.com/soulteary/nginx-formatter/actions/workflows/codecov.yml) [](https://github.com/soulteary/nginx-formatter/actions/workflows/scan.yml) [](https://github.com/soulteary/nginx-formatter/actions/workflows/release.yaml)  [](https://hub.docker.com/r/soulteary/nginx-formatter)
4 |
5 |
6 | ENGLISH | 中文文档
7 |
8 |
9 |
10 |
11 | 一款 10MB 左右的,小巧、简洁的 Nginx 格式化工具,支持命令行、WebUI、Docker、x86、ARM、macOS、Linux。
12 |
13 |
14 |
15 | ## 程序下载
16 |
17 | 从[发布页面](https://github.com/soulteary/nginx-formatter/releases)下载适用于您系统和架构的二进制文件和压缩包。
18 |
19 |
20 |
21 | 如果使用 Docker,可以使用以下命令([DockerHub](https://hub.docker.com/r/soulteary/nginx-formatter)):
22 |
23 | ```bash
24 | docker pull soulteary/nginx-formatter:latest
25 | docker pull soulteary/nginx-formatter:v1.1.1
26 | ```
27 |
28 | ## 程序使用
29 |
30 | 使用默认参数格式化当前目录中的所有的 Nginx 配置文件:
31 |
32 | ```bash
33 | ./nginx-formatter
34 | ```
35 |
36 | ### 通用玩法 (CLI & WebUI)
37 |
38 | 使用不同的缩进符号(可以使用空格、制表符、`\s`、`\t`、` `)和缩进量:
39 |
40 | ```bash
41 | ./nginx-formatter -indent=4 -char=" "
42 | ```
43 |
44 | ### 命令行用法(CLI)
45 |
46 | 格式化指定目录中的配置文件:
47 |
48 | ```bash
49 | ./nginx-formatter -input=./your-dir-path
50 | ```
51 |
52 | 在新目录中保存格式化后的配置文件:
53 |
54 | ```bash
55 | ./nginx-formatter -input=./your-dir-path -output=./your-output-dir
56 | ```
57 |
58 | ### WebUI 用法
59 |
60 | 启动 WebUI 界面:
61 |
62 | ```bash
63 | ./nginx-formatter -web
64 | ```
65 |
66 | 指定服务端口:
67 |
68 | ```bash
69 | ./nginx-formatter -web -port=8123
70 | ```
71 |
72 | ### Docker 用法
73 |
74 | 在 Docker 中使用和上面没有什么区别,比如我们启动一个在 Docker 中的 Web UI 格式化工具服务:
75 |
76 | ```bash
77 | docker run --rm -it -p 8080:8080 soulteary/nginx-formatter:v1.1.1 -web
78 | ```
79 |
80 |
81 | 如果你希望格式化当前目录的配置,可以通过类似下面的命令,来使用 Docker 中的程序:
82 |
83 | ```bash
84 | docker run --rm -it -v `pwd`:/app soulteary/nginx-formatter:v1.1.1 -input=/app
85 | ```
86 |
87 |
88 | ## 支持的完整参数列表
89 |
90 | ```bash
91 | Nginx Formatter
92 |
93 | Usage of ./nginx-formatter:
94 | -char
95 | Indent char, defualt: (default " ")
96 | -indent int
97 | Indent size, defualt: 2 (default 2)
98 | -input string
99 | Input directory
100 | -output string
101 | Output directory
102 | -port 8080
103 | WebUI Port, defualt: 8080 (default 8080)
104 | -web false
105 | Enable WebUI, defualt: false
106 | ```
107 |
108 | ## 鸣谢
109 |
110 | 格式化组件
111 |
112 | - 2016/06/24 Slomkowski 使用 Python 创建了一个 nginx 配置文件美化器,在 [Apache-2.0 许可] 下发布。
113 | - https://github.com/1connect/nginx-config-formatter (https://github.com/slomkowski/nginx-config-formatter)
114 | - 2016/08/24 Yosef 在 [Apache-2.0 许可] 下移植了 JavaScript beautifier。
115 | - https://github.com/vasilevich/nginxbeautifier
116 | - 2023/04/18,soulteary 根据 [Apache-2.0 许可] 简化程序,修复错误,提高运行速度,并允许在 Golang 中运行。
117 | - https://github.com/soulteary/nginx-formatter
118 |
119 | JavaScript 运行时组件:
120 |
121 | - Go 中的 ECMAScript 5.1(+) 实现,在 [MIT 许可]下发布。
122 | - https://github.com/dop251/goja
123 |
124 | 网络组件
125 |
126 | - Gin,HTTP Web 框架,在 [MIT 许可]下发布。
127 | - https://github.com/gin-gonic/gin
128 | - Code Mirror, 浏览器内的编辑器,在 [MIT 许可]下发布。
129 | - https://github.com/codemirror/codemirror5
130 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | All releases accept security reports, updates.
6 |
7 | | Version | Supported |
8 | | ------- | ------------------ |
9 | | * | :white_check_mark: |
10 |
11 | ## Reporting a Vulnerability
12 |
13 | If you find any security issues, welcome to file an issue or submit a PR directly.
14 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.20.0-alpine3.16 as builder
2 | RUN echo '' > /etc/apk/repositories && \
3 | echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.16/main" >> /etc/apk/repositories && \
4 | echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.16/community" >> /etc/apk/repositories && \
5 | echo "Asia/Shanghai" > /etc/timezone
6 | RUN apk add upx
7 | WORKDIR /build
8 | ENV CGO_ENABLED=0
9 | COPY ./go.mod .
10 | COPY ./go.sum .
11 | RUN go mod tidy && go mod download
12 | COPY ./ .
13 | RUN go build -ldflags "-w -s"
14 | RUN upx -9 -o nginx-formatter.minify nginx-formatter && mv nginx-formatter.minify nginx-formatter
15 |
16 | FROM alpine:3.16
17 | RUN echo '' > /etc/apk/repositories && \
18 | echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.16/main" >> /etc/apk/repositories && \
19 | echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.16/community" >> /etc/apk/repositories && \
20 | echo "Asia/Shanghai" > /etc/timezone
21 | RUN apk add openssl && rm -rf /var/cache/apk/*
22 | WORKDIR /
23 | COPY --from=Builder /build/nginx-formatter /bin/nginx-formatter
24 | ENTRYPOINT ["nginx-formatter"]
--------------------------------------------------------------------------------
/docker/Dockerfile.goreleaser:
--------------------------------------------------------------------------------
1 | FROM alpine:3.16
2 | RUN apk add openssl && rm -rf /var/cache/apk/*
3 | WORKDIR /
4 | COPY nginx-formatter /bin/nginx-formatter
5 | ENTRYPOINT ["nginx-formatter"]
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/soulteary/nginx-formatter
2 |
3 | go 1.20
4 |
5 | require (
6 | github.com/dop251/goja v0.0.0-20230427124612-428fc442ff5f
7 | github.com/gin-gonic/gin v1.9.0
8 | )
9 |
10 | require (
11 | github.com/bytedance/sonic v1.8.8 // indirect
12 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
13 | github.com/dlclark/regexp2 v1.9.0 // indirect
14 | github.com/gin-contrib/sse v0.1.0 // indirect
15 | github.com/go-playground/locales v0.14.1 // indirect
16 | github.com/go-playground/universal-translator v0.18.1 // indirect
17 | github.com/go-playground/validator/v10 v10.13.0 // indirect
18 | github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
19 | github.com/goccy/go-json v0.10.2 // indirect
20 | github.com/google/pprof v0.0.0-20230502171905-255e3b9b56de // indirect
21 | github.com/json-iterator/go v1.1.12 // indirect
22 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect
23 | github.com/leodido/go-urn v1.2.4 // indirect
24 | github.com/mattn/go-isatty v0.0.18 // indirect
25 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
26 | github.com/modern-go/reflect2 v1.0.2 // indirect
27 | github.com/pelletier/go-toml/v2 v2.0.7 // indirect
28 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
29 | github.com/ugorji/go/codec v1.2.11 // indirect
30 | golang.org/x/arch v0.3.0 // indirect
31 | golang.org/x/crypto v0.8.0 // indirect
32 | golang.org/x/net v0.9.0 // indirect
33 | golang.org/x/sys v0.8.0 // indirect
34 | golang.org/x/text v0.9.0 // indirect
35 | google.golang.org/protobuf v1.30.0 // indirect
36 | gopkg.in/yaml.v3 v3.0.1 // indirect
37 | )
38 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
2 | github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q=
3 | github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
4 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
5 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
6 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
7 | github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
8 | github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
9 | github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
10 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
11 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
12 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
13 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
14 | github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
15 | github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
16 | github.com/dlclark/regexp2 v1.9.0 h1:pTK/l/3qYIKaRXuHnEnIf7Y5NxfRPfpb7dis6/gdlVI=
17 | github.com/dlclark/regexp2 v1.9.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
18 | github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
19 | github.com/dop251/goja v0.0.0-20230427124612-428fc442ff5f h1:3Z9NjtffvA8Qoh8xjgUpPmyKawJw/mDRcJlR9oPCvqI=
20 | github.com/dop251/goja v0.0.0-20230427124612-428fc442ff5f/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
21 | github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
22 | github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
23 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
24 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
25 | github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
26 | github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
27 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
28 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
29 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
30 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
31 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
32 | github.com/go-playground/validator/v10 v10.13.0 h1:cFRQdfaSMCOSfGCCLB20MHvuoHb/s5G8L5pu2ppK5AQ=
33 | github.com/go-playground/validator/v10 v10.13.0/go.mod h1:dwu7+CG8/CtBiJFZDz4e+5Upb6OLw04gtBYw0mcG/z4=
34 | github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
35 | github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
36 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
37 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
38 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
39 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
40 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
41 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
42 | github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
43 | github.com/google/pprof v0.0.0-20230502171905-255e3b9b56de h1:6bMcLOeKoNo0+mTOb1ee3McF6CCKGixjLR3EDQY1Jik=
44 | github.com/google/pprof v0.0.0-20230502171905-255e3b9b56de/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
45 | github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
46 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
47 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
48 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
49 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
50 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
51 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
52 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
53 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
54 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
55 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
56 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
57 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
58 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
59 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
60 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
61 | github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
62 | github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
63 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
64 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
65 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
66 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
67 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
68 | github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
69 | github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
70 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
71 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
72 | github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
73 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
74 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
75 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
76 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
77 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
78 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
79 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
80 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
81 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
82 | github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
83 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
84 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
85 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
86 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
87 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
88 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
89 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
90 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
91 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
92 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
93 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
94 | golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
95 | golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
96 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
97 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
98 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
99 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
100 | golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
101 | golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
102 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
103 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
104 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
105 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
106 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
107 | golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
108 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
109 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
110 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
111 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
112 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
113 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
114 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
115 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
116 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
117 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
118 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
119 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
120 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
121 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
122 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
123 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
124 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
125 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
126 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
127 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
128 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
129 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
130 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
131 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
132 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
133 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
134 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
135 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
136 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
137 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
138 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
139 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
140 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
141 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
142 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
143 |
--------------------------------------------------------------------------------
/internal/checker/checker.go:
--------------------------------------------------------------------------------
1 | package checker
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | )
8 |
9 | func InDockerAndWorkDirIsRoot(src string) {
10 | if _, err := os.Stat("/.dockerenv"); err == nil && src == "/" {
11 | fmt.Println("To run using the Docker model, you need to specify a run directory other than the root directory.")
12 | fmt.Println("example:")
13 | fmt.Println(" docker run --rm -it -v `pwd`:/app soulteary/nginx-formatter -input=/app")
14 | os.Exit(0)
15 | }
16 | }
17 |
18 | func InputDirExist(src string) {
19 | if _, err := os.Stat(src); err != nil {
20 | fmt.Println("The directory you specified does not exist, please check the path parameters and try again.")
21 | fmt.Println("Input directory:", src)
22 | os.Exit(0)
23 | }
24 | }
25 |
26 | func FailToRun(err error) {
27 | if err != nil {
28 | log.Fatal(err)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/internal/cmd/cmd.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 |
8 | "github.com/soulteary/nginx-formatter/internal/checker"
9 | "github.com/soulteary/nginx-formatter/internal/define"
10 | )
11 |
12 | func InitArgv() (argvSrc string, argvDest string, argvIndent int, argvIndentChar string, argvWeb bool, argvPort int) {
13 | var inputDir string
14 | flag.StringVar(&inputDir, define.APP_ARGV_INPUT, define.DEFAULT_WORKDIR, "Input directory")
15 | var outputDir string
16 | flag.StringVar(&outputDir, define.APP_ARGV_OUTPUT, define.DEFAULT_WORKDIR, "Output directory")
17 | var indent int
18 | flag.IntVar(&indent, define.APP_ARGV_INDENT, define.DEFAULT_INDENT_SIZE, fmt.Sprintf("Indent size, defualt: %d", define.DEFAULT_INDENT_SIZE))
19 | var indentChar string
20 | flag.StringVar(&indentChar, define.APP_ARGV_CHAR, define.DEFAULT_INDENT_CHAR, fmt.Sprintf("Indent char, defualt: `%s`", define.DEFAULT_INDENT_CHAR))
21 |
22 | var web bool
23 | flag.BoolVar(&web, define.APP_ARGV_WEB, define.DEFAULT_WEB, fmt.Sprintf("Enable WebUI, defualt: `%v`", define.DEFAULT_WEB))
24 | var port int
25 | flag.IntVar(&port, define.APP_ARGV_PORT, define.DEFAULT_PORT, fmt.Sprintf("WebUI Port, defualt: `%d`", define.DEFAULT_PORT))
26 |
27 | flag.Parse()
28 |
29 | if inputDir == "" {
30 | dir, err := os.Getwd()
31 | checker.FailToRun(err)
32 | fmt.Println("No input directory specified, use the current working directory:", dir)
33 | argvSrc = dir
34 | } else {
35 | fmt.Println("Specify the working directory as:", inputDir)
36 | argvSrc = inputDir
37 | }
38 |
39 | if outputDir == "" {
40 | dir, err := os.Getwd()
41 | checker.FailToRun(err)
42 | fmt.Println("No output directory specified, use the current working directory:", dir)
43 | argvDest = dir
44 | } else {
45 | err := os.MkdirAll(outputDir, 0750)
46 | if err != nil {
47 | fmt.Println(err)
48 | os.Exit(1)
49 | }
50 | fmt.Println("Specify the output directory as:", inputDir)
51 | argvDest = outputDir
52 | }
53 |
54 | if indent <= 0 {
55 | fmt.Println("No output indent size specified, use the default value:", define.DEFAULT_INDENT_SIZE)
56 | argvIndent = define.DEFAULT_INDENT_SIZE
57 | } else {
58 | fmt.Println("Specify the indent size as:", indent)
59 | argvIndent = indent
60 | }
61 |
62 | if indentChar == "" {
63 | argvIndentChar = define.DEFAULT_INDENT_CHAR
64 | fmt.Printf("No output indent char specified, use the default value: `%s`\n", define.DISPLAY_INDENT_CHARS[define.DEFAULT_INDENT_CHAR])
65 | } else {
66 | if !(indentChar == "\t" || indentChar == " " || indentChar == "\\s") {
67 | indentChar = define.DEFAULT_INDENT_CHAR
68 | fmt.Printf("Specify the indent char not support, use the default value: `%s`\n", define.DISPLAY_INDENT_CHARS[define.DEFAULT_INDENT_CHAR])
69 | }
70 | argvIndentChar = indentChar
71 | display, ok := define.DISPLAY_INDENT_CHARS[indentChar]
72 | if ok {
73 | fmt.Printf("Specify the indent char as: `%s`\n", display)
74 | } else {
75 | fmt.Printf("Specify the indent char as: `%s`\n", indentChar)
76 | }
77 | }
78 |
79 | if web {
80 | argvWeb = true
81 | if port <= 1024 || port >= 65535 {
82 | fmt.Println("Please set the port above 1024 and the port within 65535")
83 | fmt.Printf("use the default value: `%d`\n", define.DEFAULT_PORT)
84 | argvPort = define.DEFAULT_PORT
85 | } else {
86 | argvPort = port
87 | fmt.Printf("Specify the indent char as: `%d`\n", port)
88 | }
89 | fmt.Printf("Enable WebUI, please visit http://localhost:%d\n", port)
90 | } else {
91 | argvWeb = false
92 | }
93 |
94 | fmt.Println()
95 | return argvSrc, argvDest, argvIndent, argvIndentChar, argvWeb, argvPort
96 | }
97 |
--------------------------------------------------------------------------------
/internal/define/define.go:
--------------------------------------------------------------------------------
1 | package define
2 |
3 | // default config
4 | const (
5 | // common config
6 | DEFAULT_INDENT_SIZE = 2
7 | DEFAULT_INDENT_CHAR = " "
8 | DEFAULT_WORKDIR = ""
9 | // web config
10 | DEFAULT_PORT = 8080
11 | DEFAULT_WEB = false
12 | )
13 |
14 | var DISPLAY_INDENT_CHARS = map[string]string{
15 | " ": "[SPACE]",
16 | "\\s": "[SPACE]",
17 | "\t": "[TAB]",
18 | }
19 |
20 | const (
21 | APP_ARGV_INPUT = "input"
22 | APP_ARGV_OUTPUT = "output"
23 | APP_ARGV_INDENT = "indent"
24 | APP_ARGV_CHAR = "char"
25 | // web flags
26 | APP_ARGV_PORT = "port"
27 | APP_ARGV_WEB = "web"
28 | )
29 |
--------------------------------------------------------------------------------
/internal/formatter/beautifier.go:
--------------------------------------------------------------------------------
1 | package formatter
2 |
3 | import (
4 | _ "embed"
5 | )
6 |
7 | //go:embed beautifier.js
8 | var JS_FORMATTER string
9 |
--------------------------------------------------------------------------------
/internal/formatter/beautifier.js:
--------------------------------------------------------------------------------
1 | /**
2 | * - Soulteary Modify the JavaScript version for golang execution, under [Apache-2.0 license], 18/04/2023:
3 | * - simplify the program, fix bugs, improve running speed, and allow running in golang
4 | * - https://github.com/soulteary/nginx-formatter
5 | *
6 | * History:
7 | * - Yosef Ported the JavaScript beautifier under [Apache-2.0 license], 24/08/2016
8 | * - https://github.com/vasilevich/nginxbeautifier
9 | * - Slomkowski Created a beautifier for nginx config files with Python under [Apache-2.0 license], 24/06/2016
10 | * - https://github.com/1connect/nginx-config-formatter (https://github.com/slomkowski/nginx-config-formatter)
11 | */
12 |
13 | /**
14 | * Grabs text in between two seperators seperator1 thetextIwant seperator2
15 | * @param {string} input String to seperate
16 | * @param {string} seperator1 The first seperator to use
17 | * @param {string} seperator2 The second seperator to use
18 | * @return {string}
19 | */
20 | function extractTextBySeperator(input, seperator1, seperator2) {
21 | if (seperator2 == undefined) seperator2 = seperator1;
22 | var seperator1Regex = new RegExp(seperator1);
23 | var seperator2Regex = new RegExp(seperator2);
24 | var catchRegex = new RegExp(seperator1 + "(.*?)" + seperator2);
25 | if (seperator1Regex.test(input) && seperator2Regex.test(input)) {
26 | return input.match(catchRegex)[1];
27 | } else {
28 | return "";
29 | }
30 | }
31 |
32 | /**
33 | * Grabs text in between two seperators seperator1 thetextIwant seperator2
34 | * @param {string} input String to seperate
35 | * @param {string} seperator1 The first seperator to use
36 | * @param {string} seperator2 The second seperator to use
37 | * @return {object}
38 | */
39 | function extractAllPossibleText(input, seperator1, seperator2) {
40 | if (seperator2 == undefined) seperator2 = seperator1;
41 | var extracted = {};
42 | var textInBetween;
43 | var cnt = 0;
44 | var seperator1CharCode = seperator1.length > 0 ? seperator1.charCodeAt(0) : "";
45 | var seperator2CharCode = seperator2.length > 0 ? seperator2.charCodeAt(0) : "";
46 | while ((textInBetween = extractTextBySeperator(input, seperator1, seperator2)) != "") {
47 | var placeHolder = "#$#%#$#placeholder" + cnt + "" + seperator1CharCode + "" + seperator2CharCode + "#$#%#$#";
48 | extracted[placeHolder] = seperator1 + textInBetween + seperator2;
49 | input = input.replace(extracted[placeHolder], placeHolder);
50 | cnt++;
51 | }
52 | return {
53 | filteredInput: input,
54 | extracted: extracted,
55 | getRestored: function () {
56 | var textToFix = this.filteredInput;
57 | for (var key in extracted) {
58 | textToFix = textToFix.replace(key, extracted[key]);
59 | }
60 | return textToFix;
61 | },
62 | };
63 | }
64 |
65 | /**
66 | * @param {string} single_line the whole nginx config
67 | * @return {string} stripped out string without multi spaces
68 | */
69 | function strip_line(single_line) {
70 | //"""Strips the line and replaces neighbouring whitespaces with single space (except when within quotation marks)."""
71 | //trim the line before and after
72 | var trimmed = single_line.trim();
73 | //get text without any quatation marks(text foudn with quatation marks is replaced with a placeholder)
74 | var removedDoubleQuatations = extractAllPossibleText(trimmed, '"', '"');
75 | //replace multi spaces with single spaces, but skip in sub_filter directive
76 | if (!removedDoubleQuatations.filteredInput.includes("sub_filter")) {
77 | removedDoubleQuatations.filteredInput = removedDoubleQuatations.filteredInput.replace(/\s\s+/g, " ");
78 | }
79 | //restore anything of quatation marks
80 | return removedDoubleQuatations.getRestored();
81 | }
82 |
83 | /**
84 | * @param {string} configContents the whole nginx config
85 | */
86 | function clean_lines(configContents) {
87 | var splittedByLines = configContents.split(/\r\n|\r|\n/g);
88 | //put { } on their own seperate lines
89 | //trim the spaces before and after each line
90 | //trim multi spaces into single spaces
91 | //trim multi lines into two
92 |
93 | for (var index = 0, newline = 0; index < splittedByLines.length; index++) {
94 | splittedByLines[index] = splittedByLines[index].trim();
95 | if (splittedByLines[index] != "") {
96 | splittedByLines[index] = splittedByLines[index].replace(/\{\}/g, `{ }`);
97 | }
98 |
99 | if (!splittedByLines[index].startsWith("#") && splittedByLines[index] != "") {
100 | newline = 0;
101 | var line = (splittedByLines[index] = strip_line(splittedByLines[index]));
102 | if (line != "}" && line != "{" && !(line.includes("('{") || line.includes("}')") || line.includes("'{'") || line.includes("'}'"))) {
103 | var startOfComment = line.indexOf("#");
104 | var code = startOfComment >= 0 ? line.slice(0, startOfComment) : line;
105 |
106 | var removedDoubleQuatations = extractAllPossibleText(code, '"', '"');
107 | code = removedDoubleQuatations.filteredInput;
108 |
109 | var startOfParanthesis = code.indexOf("}");
110 | if (startOfParanthesis >= 0) {
111 | if (startOfParanthesis > 0) {
112 | splittedByLines[index] = strip_line(code.slice(0, startOfParanthesis - 1));
113 | splittedByLines.splice(index + 1, 0, "}");
114 | }
115 | var l2 = strip_line(code.slice(startOfParanthesis + 1));
116 | if (l2 != "") splittedByLines.splice(index + 2, 0, l2);
117 | code = splittedByLines[index];
118 | }
119 | var endOfParanthesis = code.indexOf("{");
120 | if (endOfParanthesis >= 0) {
121 | splittedByLines[index] = strip_line(code.slice(0, endOfParanthesis));
122 | splittedByLines.splice(index + 1, 0, "{");
123 | var l2 = strip_line(code.slice(endOfParanthesis + 1));
124 | if (l2 != "") splittedByLines.splice(index + 2, 0, l2);
125 | }
126 |
127 | removedDoubleQuatations.filteredInput = splittedByLines[index];
128 | line = removedDoubleQuatations.getRestored();
129 | splittedByLines[index] = line;
130 | }
131 | }
132 | //remove more than two newlines
133 | else if (splittedByLines[index] == "") {
134 | if (newline++ >= 2) {
135 | splittedByLines.splice(index, 1);
136 | index--;
137 | }
138 | }
139 | }
140 | return splittedByLines;
141 | }
142 |
143 | function join_opening_bracket(lines) {
144 | for (var i = 0; i < lines.length; i++) {
145 | var line = lines[i];
146 | if (line == "{") {
147 | //just make sure we don't put anything before 0
148 | if (i >= 1) {
149 | lines[i] = lines[i - 1] + " {";
150 | lines.splice(i - 1, 1);
151 | }
152 | }
153 | }
154 | return lines;
155 | }
156 |
157 | function fold_empty_brackets(lines) {
158 | return lines
159 | .join("\n")
160 | .replace(new RegExp(`\\s+{[\\s\\n\\r]*?}`, "gm"), " { }")
161 | .replace(/\n{3,}/gm, "\n\n");
162 | }
163 |
164 | function add_empty_line_after_nginx_directives(lines) {
165 | let clone = lines.reverse();
166 | let output = [];
167 | for (let i = 0, j = clone.length; i < j; i++) {
168 | let current = (clone[i] + "").trim();
169 | let next = (clone[i + 1] + "").trim();
170 | if (next && !next.startsWith("}") && current.startsWith("}")) output.push("");
171 | output.push(clone[i]);
172 | }
173 | return output.reverse();
174 | }
175 |
176 | function fixDollarVar(lines) {
177 | const placeHolder = `[dollar]`;
178 | return lines.map((line) => {
179 | while (line.indexOf(placeHolder) !== -1) {
180 | line = line.replace(placeHolder, "$");
181 | }
182 | return line;
183 | });
184 | }
185 |
186 | var options = { INDENTATION: "\t" };
187 |
188 | function perform_indentation(lines) {
189 | var indented_lines, current_indent, line;
190 | ("Indents the lines according to their nesting level determined by curly brackets.");
191 | indented_lines = [];
192 | current_indent = 0;
193 | var iterator1 = lines;
194 | for (var index1 = 0; index1 < iterator1.length; index1++) {
195 | line = iterator1[index1];
196 | if (!line.startsWith("#") && /.*?\}(\s*#.*)?$/.test(line) && current_indent > 0) {
197 | current_indent -= 1;
198 | }
199 | if (line !== "") {
200 | indented_lines.push(options.INDENTATION.repeat(current_indent) + line);
201 | } else {
202 | indented_lines.push("");
203 | }
204 | if (!line.startsWith("#") && /.*?\{(\s*#.*)?$/.test(line)) {
205 | current_indent += 1;
206 | }
207 | }
208 | return indented_lines;
209 | }
210 |
211 | function FormatNginxConf(text, indentSize = 2, indentChar = " ") {
212 | options["INDENTATION"] = indentChar.repeat(indentSize);
213 |
214 | let lines = clean_lines(text);
215 | lines = join_opening_bracket(lines);
216 | lines = perform_indentation(lines);
217 | lines = add_empty_line_after_nginx_directives(lines);
218 | lines = fixDollarVar(lines);
219 | return fold_empty_brackets(lines);
220 | }
221 |
--------------------------------------------------------------------------------
/internal/formatter/formatter.go:
--------------------------------------------------------------------------------
1 | package formatter
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/dop251/goja"
7 | )
8 |
9 | func Formatter(s string, indent int, char string) (string, error) {
10 | if s == "" {
11 | return "", nil
12 | }
13 | vm := goja.New()
14 | v, err := vm.RunString(fmt.Sprintf("%s;FormatNginxConf(`%s`, %d, `%s`)", JS_FORMATTER, s, indent, char))
15 | if err != nil {
16 | return "", err
17 | }
18 | return v.String(), nil
19 | }
20 |
--------------------------------------------------------------------------------
/internal/formatter/formatter_test.go:
--------------------------------------------------------------------------------
1 | package formatter_test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/soulteary/nginx-formatter/internal/formatter"
7 | )
8 |
9 | func TestFormatter(t *testing.T) {
10 |
11 | const TestData = `
12 | load_module modules/ngx_http_js_module.so;
13 |
14 | events { }
15 |
16 | http {
17 | js_path "/etc/nginx/njs/";
18 |
19 | js_import main from http/api/set_keyval.js;
20 |
21 | keyval_zone zone=foo:10m;
22 |
23 | server {
24 | listen 80;
25 |
26 | location /keyval {
27 | js_content main.set_keyval;
28 | }
29 | location /api {
30 | internal;
31 | api write=on;
32 | }
33 | location /api/ro {
34 | api;
35 | }
36 | }
37 | }`
38 |
39 | const TestExpected = `
40 | load_module modules/ngx_http_js_module.so;
41 |
42 | events { }
43 |
44 | http {
45 | js_path "/etc/nginx/njs/";
46 |
47 | js_import main from http/api/set_keyval.js;
48 |
49 | keyval_zone zone=foo:10m;
50 |
51 | server {
52 | listen 80;
53 |
54 | location /keyval {
55 | js_content main.set_keyval;
56 | }
57 |
58 | location /api {
59 | internal;
60 | api write=on;
61 | }
62 |
63 | location /api/ro {
64 | api;
65 | }
66 |
67 | }
68 | }`
69 |
70 | result, err := formatter.Formatter(TestData, 4, " ")
71 | if err != nil {
72 | t.Errorf("formatter error: %v\n", err)
73 | }
74 |
75 | if result != TestExpected {
76 | t.Error("formatter result not expected", result, TestExpected)
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/internal/server/assets.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | _ "embed"
5 | "regexp"
6 | "strings"
7 | )
8 |
9 | //go:embed assets/base.css
10 | var PAGE_STYLESTHEET string
11 | var CACHE_STYLESHEET = []byte(PAGE_STYLESTHEET)
12 |
13 | //go:embed assets/base.js
14 | var PAGE_SCRIPT string
15 | var CACHE_SCRIPT = []byte(PAGE_SCRIPT)
16 |
17 | //go:embed assets/index.html
18 | var PAGE_DOCUMENT string
19 | var CACHE_DOCUMENT string
20 |
21 | var REGEXP_UPDATE_CODE = regexp.MustCompile(`(?m)