feat(Yaml Viewer): add parsing validation
Add parsing validations Fix #540
This commit is contained in:
		
							parent
							
								
									d3b32cc14e
								
							
						
					
					
						commit
						fd0a723f06
					
				
							
								
								
									
										3
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -126,6 +126,7 @@ declare module '@vue/runtime-core' { | ||||
|     MenuLayout: typeof import('./src/components/MenuLayout.vue')['default'] | ||||
|     MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default'] | ||||
|     MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] | ||||
|     NAlert: typeof import('naive-ui')['NAlert'] | ||||
|     NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] | ||||
|     NCode: typeof import('naive-ui')['NCode'] | ||||
|     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] | ||||
| @ -145,6 +146,7 @@ declare module '@vue/runtime-core' { | ||||
|     NMenu: typeof import('naive-ui')['NMenu'] | ||||
|     NScrollbar: typeof import('naive-ui')['NScrollbar'] | ||||
|     NSpin: typeof import('naive-ui')['NSpin'] | ||||
|     NSwitch: typeof import('naive-ui')['NSwitch'] | ||||
|     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] | ||||
|     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] | ||||
|     PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default'] | ||||
| @ -159,6 +161,7 @@ declare module '@vue/runtime-core' { | ||||
|     RouterLink: typeof import('vue-router')['RouterLink'] | ||||
|     RouterView: typeof import('vue-router')['RouterView'] | ||||
|     RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default'] | ||||
|     SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default'] | ||||
|     SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default'] | ||||
|     SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default'] | ||||
|     SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default'] | ||||
|  | ||||
| @ -51,6 +51,7 @@ | ||||
|     "change-case": "^4.1.2", | ||||
|     "colord": "^2.9.3", | ||||
|     "composerize-ts": "^0.6.2", | ||||
|     "composeverter": "^1.7.2", | ||||
|     "country-code-lookup": "^0.1.0", | ||||
|     "cron-validator": "^1.3.1", | ||||
|     "cronstrue": "^2.26.0", | ||||
|  | ||||
							
								
								
									
										48
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										48
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -53,6 +53,9 @@ dependencies: | ||||
|   composerize-ts: | ||||
|     specifier: ^0.6.2 | ||||
|     version: 0.6.2 | ||||
|   composeverter: | ||||
|     specifier: ^1.7.2 | ||||
|     version: 1.7.2 | ||||
|   country-code-lookup: | ||||
|     specifier: ^0.1.0 | ||||
|     version: 0.1.0 | ||||
| @ -3351,7 +3354,7 @@ packages: | ||||
|     dependencies: | ||||
|       '@unhead/dom': 0.5.1 | ||||
|       '@unhead/schema': 0.5.1 | ||||
|       '@vueuse/shared': 10.7.2(vue@3.3.4) | ||||
|       '@vueuse/shared': 10.9.0(vue@3.3.4) | ||||
|       unhead: 0.5.1 | ||||
|       vue: 3.3.4 | ||||
|     transitivePeerDependencies: | ||||
| @ -3993,10 +3996,10 @@ packages: | ||||
|       - vue | ||||
|     dev: false | ||||
| 
 | ||||
|   /@vueuse/shared@10.7.2(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==} | ||||
|   /@vueuse/shared@10.9.0(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==} | ||||
|     dependencies: | ||||
|       vue-demi: 0.14.6(vue@3.3.4) | ||||
|       vue-demi: 0.14.7(vue@3.3.4) | ||||
|     transitivePeerDependencies: | ||||
|       - '@vue/composition-api' | ||||
|       - vue | ||||
| @ -4051,6 +4054,14 @@ packages: | ||||
|       - supports-color | ||||
|     dev: true | ||||
| 
 | ||||
|   /ajv-errors@3.0.0(ajv@8.12.0): | ||||
|     resolution: {integrity: sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==} | ||||
|     peerDependencies: | ||||
|       ajv: ^8.0.1 | ||||
|     dependencies: | ||||
|       ajv: 8.12.0 | ||||
|     dev: false | ||||
| 
 | ||||
|   /ajv@6.12.6: | ||||
|     resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} | ||||
|     dependencies: | ||||
| @ -4067,7 +4078,6 @@ packages: | ||||
|       json-schema-traverse: 1.0.0 | ||||
|       require-from-string: 2.0.2 | ||||
|       uri-js: 4.4.1 | ||||
|     dev: true | ||||
| 
 | ||||
|   /ansi-colors@4.1.3: | ||||
|     resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} | ||||
| @ -4560,6 +4570,15 @@ packages: | ||||
|       yamljs: 0.3.0 | ||||
|     dev: false | ||||
| 
 | ||||
|   /composeverter@1.7.2: | ||||
|     resolution: {integrity: sha512-gRSCkSU8wJQvmTHQ23wBd2gCdUsns95k3lL7YszZBu5EyA3eac46mI7nVgvk1pZTM0mOmj/E5yurtdYkuVNZRA==} | ||||
|     dependencies: | ||||
|       ajv: 8.12.0 | ||||
|       ajv-errors: 3.0.0(ajv@8.12.0) | ||||
|       core-js: 2.6.12 | ||||
|       yaml: 1.10.2 | ||||
|     dev: false | ||||
| 
 | ||||
|   /concat-map@0.0.1: | ||||
|     resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} | ||||
| 
 | ||||
| @ -4613,6 +4632,12 @@ packages: | ||||
|       browserslist: 4.22.1 | ||||
|     dev: true | ||||
| 
 | ||||
|   /core-js@2.6.12: | ||||
|     resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} | ||||
|     deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. | ||||
|     requiresBuild: true | ||||
|     dev: false | ||||
| 
 | ||||
|   /country-code-lookup@0.1.0: | ||||
|     resolution: {integrity: sha512-IOI66HEG+8bXfWPy+sTzuN7161vmDZOHg1wgIPFf3WfD73FeLajnn6C+fnxOIa9RL1WRBDMXQQWW/FOaOYaQ3w==} | ||||
|     dev: false | ||||
| @ -6571,7 +6596,6 @@ packages: | ||||
| 
 | ||||
|   /json-schema-traverse@1.0.0: | ||||
|     resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} | ||||
|     dev: true | ||||
| 
 | ||||
|   /json-schema@0.4.0: | ||||
|     resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} | ||||
| @ -7666,7 +7690,6 @@ packages: | ||||
|   /punycode@2.3.0: | ||||
|     resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} | ||||
|     engines: {node: '>=6'} | ||||
|     dev: true | ||||
| 
 | ||||
|   /punycode@2.3.1: | ||||
|     resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} | ||||
| @ -7814,7 +7837,6 @@ packages: | ||||
|   /require-from-string@2.0.2: | ||||
|     resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} | ||||
|     engines: {node: '>=0.10.0'} | ||||
|     dev: true | ||||
| 
 | ||||
|   /require-main-filename@2.0.0: | ||||
|     resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} | ||||
| @ -8913,7 +8935,6 @@ packages: | ||||
|     resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} | ||||
|     dependencies: | ||||
|       punycode: 2.3.0 | ||||
|     dev: true | ||||
| 
 | ||||
|   /url-parse@1.5.10: | ||||
|     resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} | ||||
| @ -9151,8 +9172,8 @@ packages: | ||||
|       vue: 3.3.4 | ||||
|     dev: false | ||||
| 
 | ||||
|   /vue-demi@0.14.6(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} | ||||
|   /vue-demi@0.14.7(vue@3.3.4): | ||||
|     resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} | ||||
|     engines: {node: '>=12'} | ||||
|     hasBin: true | ||||
|     requiresBuild: true | ||||
| @ -9579,6 +9600,11 @@ packages: | ||||
|       yaml: 2.2.1 | ||||
|     dev: true | ||||
| 
 | ||||
|   /yaml@1.10.2: | ||||
|     resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} | ||||
|     engines: {node: '>= 6'} | ||||
|     dev: false | ||||
| 
 | ||||
|   /yaml@2.2.1: | ||||
|     resolution: {integrity: sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==} | ||||
|     engines: {node: '>= 14'} | ||||
|  | ||||
							
								
								
									
										20
									
								
								src/tools/yaml-viewer/composeverter.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/tools/yaml-viewer/composeverter.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,20 @@ | ||||
| declare module 'composeverter' { | ||||
|     interface Configuration { | ||||
|       expandVolumes?: boolean; | ||||
|       expandPorts?: boolean; | ||||
|       indent?: number; | ||||
|     } | ||||
|     interface DockerComposeValidatioError { | ||||
|       line?: number; | ||||
|       message: string; | ||||
|       helpLink?: string; | ||||
|     } | ||||
|     export function validateDockerComposeToCommonSpec(content: string): DockerComposeValidatioError[]; | ||||
|     export function migrateFromV2xToV3x(content: string, configuration?: Configuration = null): string; | ||||
|     export function migrateFromV3xToV2x(content: string, configuration?: Configuration = null): string; | ||||
|     export function migrateFromV1ToV2x(content: string, configuration?: Configuration = null): string; | ||||
|     export function migrateToCommonSpec(content: string, configuration?: Configuration = null): string; | ||||
|     export function migrateFromV2xToV3x(content: string, configuration?: Configuration = null): string; | ||||
|     export function getDockerComposeSchemaWithoutFormats(): object; | ||||
|     export function yamlParse(content: string): object; | ||||
|   } | ||||
| @ -1,5 +1,5 @@ | ||||
| import { type MaybeRef, get } from '@vueuse/core'; | ||||
| 
 | ||||
| import { yamlParse } from 'composeverter'; | ||||
| import yaml from 'yaml'; | ||||
| 
 | ||||
| export { formatYaml }; | ||||
| @ -13,7 +13,7 @@ function formatYaml({ | ||||
|   sortKeys?: MaybeRef<boolean> | ||||
|   indentSize?: MaybeRef<number> | ||||
| }) { | ||||
|   const parsedYaml = yaml.parse(get(rawYaml)); | ||||
|   const parsedYaml = yamlParse(get(rawYaml)); | ||||
| 
 | ||||
|   const formattedYAML = yaml.stringify(parsedYaml, { | ||||
|     sortMapEntries: get(sortKeys), | ||||
|  | ||||
| @ -1,72 +1,80 @@ | ||||
| <script setup lang="ts"> | ||||
| import yaml from 'yaml'; | ||||
| import { useStorage } from '@vueuse/core'; | ||||
| import { formatYaml } from './yaml-models'; | ||||
| import { withDefaultOnError } from '@/utils/defaults'; | ||||
| import { useValidation } from '@/composable/validation'; | ||||
| import TextareaCopyable from '@/components/TextareaCopyable.vue'; | ||||
| 
 | ||||
| const inputElement = ref<HTMLElement>(); | ||||
| 
 | ||||
| const rawYaml = useStorage('yaml-prettify:raw-yaml', ''); | ||||
| const indentSize = useStorage('yaml-prettify:indent-size', 2); | ||||
| const sortKeys = useStorage('yaml-prettify:sort-keys', false); | ||||
| 
 | ||||
| const cleanYaml = computed(() => withDefaultOnError(() => formatYaml({ rawYaml, indentSize, sortKeys }), '')); | ||||
| 
 | ||||
| const rawYamlValidation = useValidation({ | ||||
|   source: rawYaml, | ||||
|   rules: [ | ||||
|     { | ||||
|       validator: v => v === '' || yaml.parse(v), | ||||
|       message: 'Provided YAML is not valid.', | ||||
|     }, | ||||
|   ], | ||||
| const yamlFormattingResult = computed(() => { | ||||
|   try { | ||||
|     return { yaml: formatYaml({ rawYaml, indentSize, sortKeys }), errors: [] }; | ||||
|   } | ||||
|   catch (e: any) { | ||||
|     return { yaml: '#see error messages', errors: e.toString().split('\n') }; | ||||
|   } | ||||
| }); | ||||
| const errors = computed(() => yamlFormattingResult.value.errors); | ||||
| const cleanYaml = computed(() => yamlFormattingResult.value.yaml); | ||||
| 
 | ||||
| const MONACO_EDITOR_OPTIONS = { | ||||
|   automaticLayout: true, | ||||
|   formatOnType: true, | ||||
|   formatOnPaste: true, | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div style="flex: 0 0 100%"> | ||||
|     <div style="margin: 0 auto; max-width: 600px" flex justify-center gap-3> | ||||
|       <n-form-item label="Sort keys :" label-placement="left" label-width="100"> | ||||
|         <n-switch v-model:value="sortKeys" /> | ||||
|       </n-form-item> | ||||
|       <n-form-item label="Indent size :" label-placement="left" label-width="100" :show-feedback="false"> | ||||
|         <n-input-number v-model:value="indentSize" min="1" max="10" style="width: 100px" /> | ||||
|       </n-form-item> | ||||
|   <div max-w-600> | ||||
|     <div style="flex: 0 0 100%"> | ||||
|       <div style="margin: 0 auto; max-width: 600px" flex justify-center gap-3> | ||||
|         <n-form-item label="Sort keys :" label-placement="left" label-width="100"> | ||||
|           <n-switch v-model:value="sortKeys" /> | ||||
|         </n-form-item> | ||||
|         <n-form-item label="Indent size :" label-placement="left" label-width="100" :show-feedback="false"> | ||||
|           <n-input-number v-model:value="indentSize" min="1" max="10" style="width: 100px" /> | ||||
|         </n-form-item> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
|   <n-form-item | ||||
|     label="Your raw YAML" | ||||
|     :feedback="rawYamlValidation.message" | ||||
|     :validation-status="rawYamlValidation.status" | ||||
|   > | ||||
|     <c-input-text | ||||
|       ref="inputElement" | ||||
|       v-model:value="rawYaml" | ||||
|       placeholder="Paste your raw YAML here..." | ||||
|       rows="20" | ||||
|       multiline | ||||
|       autocomplete="off" | ||||
|       autocorrect="off" | ||||
|       autocapitalize="off" | ||||
|       spellcheck="false" | ||||
|       monospace | ||||
|     /> | ||||
|   </n-form-item> | ||||
|   <n-form-item label="Prettified version of your YAML"> | ||||
|     <TextareaCopyable :value="cleanYaml" language="yaml" :follow-height-of="inputElement" /> | ||||
|   </n-form-item> | ||||
|     <c-label label="Your raw YAML:"> | ||||
|       <div relative w-full> | ||||
|         <c-monaco-editor | ||||
|           v-model:value="rawYaml" | ||||
|           theme="vs-dark" | ||||
|           language="yaml" | ||||
|           height="250px" | ||||
|           :options="MONACO_EDITOR_OPTIONS" | ||||
|         /> | ||||
|       </div> | ||||
|     </c-label> | ||||
| 
 | ||||
|     <div v-if="errors.length > 0"> | ||||
|       <n-alert title="The following errors occured" type="error" mt-5> | ||||
|         <ul> | ||||
|           <li v-for="(message, index) of errors" :key="index"> | ||||
|             {{ message }} | ||||
|           </li> | ||||
|         </ul> | ||||
|       </n-alert> | ||||
|     </div> | ||||
| 
 | ||||
|     <n-divider /> | ||||
| 
 | ||||
|     <n-form-item label="Prettified version of your YAML"> | ||||
|       <TextareaCopyable :value="cleanYaml" language="yaml" /> | ||||
|     </n-form-item> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <style lang="less" scoped> | ||||
| .result-card { | ||||
|   position: relative; | ||||
|   .copy-button { | ||||
|   <style lang="less" scoped> | ||||
|     .result-card { | ||||
|     position: relative; | ||||
|     .copy-button { | ||||
|     position: absolute; | ||||
|     top: 10px; | ||||
|     right: 10px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
|     } | ||||
|     } | ||||
|   </style> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user