This commit is contained in:
sharevb 2024-05-11 11:37:39 +08:00 committed by GitHub
commit e90dd641c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 306 additions and 11 deletions

1
components.d.ts vendored
View file

@ -50,6 +50,7 @@ declare module '@vue/runtime-core' {
'CModal.demo': typeof import('./src/ui/c-modal/c-modal.demo.vue')['default']
CModalValue: typeof import('./src/ui/c-modal-value/c-modal-value.vue')['default']
'CModalValue.demo': typeof import('./src/ui/c-modal-value/c-modal-value.demo.vue')['default']
CMonacoEditor: typeof import('./src/ui/c-monaco-editor/c-monaco-editor.vue')['default']
CollapsibleToolMenu: typeof import('./src/components/CollapsibleToolMenu.vue')['default']
ColorConverter: typeof import('./src/tools/color-converter/color-converter.vue')['default']
ColoredCard: typeof import('./src/components/ColoredCard.vue')['default']

View file

@ -35,6 +35,7 @@
"release": "node ./scripts/release.mjs"
},
"dependencies": {
"@guolao/vue-monaco-editor": "^1.4.1",
"@it-tools/bip39": "^0.0.4",
"@it-tools/oggen": "^1.3.0",
"@sindresorhus/slugify": "^2.2.1",
@ -51,6 +52,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",

80
pnpm-lock.yaml generated
View file

@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false
dependencies:
'@guolao/vue-monaco-editor':
specifier: ^1.4.1
version: 1.5.1(monaco-editor@0.43.0)(vue@3.3.4)
'@it-tools/bip39':
specifier: ^0.0.4
version: 0.0.4
@ -53,6 +56,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
@ -2150,6 +2156,22 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
/@guolao/vue-monaco-editor@1.5.1(monaco-editor@0.43.0)(vue@3.3.4):
resolution: {integrity: sha512-nhbQHDAwsxrdH/yitcrBgAkN8Cae0IEiYe/M3LWK8bSJRfapkbMyfTHE6Gcxsxa/6efSUZAPDo8dJWBDx5GZyA==}
peerDependencies:
'@vue/composition-api': ^1.7.1
monaco-editor: '>=0.43.0'
vue: ^2.6.14 || >=3.0.0
peerDependenciesMeta:
'@vue/composition-api':
optional: true
dependencies:
'@monaco-editor/loader': 1.4.0(monaco-editor@0.43.0)
monaco-editor: 0.43.0
vue: 3.3.4
vue-demi: 0.14.7(vue@3.3.4)
dev: false
/@humanwhocodes/config-array@0.11.10:
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
engines: {node: '>=10.10.0'}
@ -2417,6 +2439,15 @@ packages:
resolution: {integrity: sha512-mrC4y8n88BYvgcgzq9bvTlDgFyi2zuvzmPilRvRc3Uz1iIvq8mDhxJ0rHKFUNzPEScpDvJdIujqiDrulMqiudA==}
dev: true
/@monaco-editor/loader@1.4.0(monaco-editor@0.43.0):
resolution: {integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==}
peerDependencies:
monaco-editor: '>= 0.21.0 < 1'
dependencies:
monaco-editor: 0.43.0
state-local: 1.0.7
dev: false
/@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -3351,7 +3382,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 +4024,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 +4082,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 +4106,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 +4598,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 +4660,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 +6624,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 +7718,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 +7865,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==}
@ -8196,6 +8246,10 @@ packages:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
dev: true
/state-local@1.0.7:
resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
dev: false
/std-env@3.3.3:
resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==}
dev: true
@ -8913,7 +8967,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 +9204,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 +9632,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'}

View file

@ -2,6 +2,9 @@ import { createApp } from 'vue';
import { createPinia } from 'pinia';
import { createHead } from '@vueuse/head';
import { install as VueMonacoEditorPlugin, loader } from '@guolao/vue-monaco-editor';
import * as monaco from 'monaco-editor';
import { registerSW } from 'virtual:pwa-register';
import { plausible } from './plugins/plausible.plugin';
@ -13,10 +16,14 @@ import App from './App.vue';
import router from './router';
import { i18nPlugin } from './plugins/i18n.plugin';
// loaded monaco-editor from `node_modules`
loader.config({ monaco });
registerSW();
const app = createApp(App);
app.use(VueMonacoEditorPlugin);
app.use(createPinia());
app.use(createHead());
app.use(i18nPlugin);

View file

@ -0,0 +1,19 @@
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;
}

View file

@ -0,0 +1,70 @@
<script setup lang="ts">
import {
validateDockerComposeToCommonSpec,
} from 'composeverter';
const dockerCompose = ref(
`version: '3.3'
services:
nginx:
ports:
- '80:80'
volumes:
- '/var/run/docker.sock:/tmp/docker.sock:ro'
restart: always
logging:
options:
max-size: 1g
image: nginx`,
);
const conversionResult = computed(() => {
try {
return validateDockerComposeToCommonSpec(dockerCompose.value);
}
catch (e: any) {
return e.toString().split('\n').map((err: string) => ({ line: -1, message: err, helpLink: '' }));
}
});
const errors = computed(() => conversionResult.value);
const MONACO_EDITOR_OPTIONS = {
automaticLayout: true,
formatOnType: true,
formatOnPaste: true,
};
</script>
<template>
<div>
<c-label label="Paste your Docker Compose file content:">
<div relative w-full>
<c-monaco-editor
v-model:value="dockerCompose"
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.message }} (<n-a v-if="message.helpLink" target="_blank" rel="noreferer noopener">
See Docker Compose help
</n-a>)
</li>
</ul>
</n-alert>
</div>
<div v-else>
<n-alert type="success" mt-5>
Validation successful!
</n-alert>
</div>
</div>
</template>

View file

@ -0,0 +1,12 @@
import { BrandDocker } from '@vicons/tabler';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'Docker Compose Validator',
path: '/docker-compose-validator',
description: 'Validate Docker Compose files against CommonSpec schema',
keywords: ['docker', 'compose', 'validator', 'commonspec'],
component: () => import('./docker-compose-validator.vue'),
icon: BrandDocker,
createdAt: new Date('2024-01-25'),
});

View file

@ -6,6 +6,7 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer';
import { tool as textToUnicode } from './text-to-unicode';
import { tool as safelinkDecoder } from './safelink-decoder';
import { tool as dockerComposeValidator } from './docker-compose-validator';
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
import { tool as numeronymGenerator } from './numeronym-generator';
import { tool as macAddressGenerator } from './mac-address-generator';
@ -146,6 +147,7 @@ export const toolsByCategory: ToolCategory[] = [
sqlPrettify,
chmodCalculator,
dockerRunToDockerComposeConverter,
dockerComposeValidator,
xmlFormatter,
yamlViewer,
],

View file

@ -0,0 +1,124 @@
<script setup lang="ts">
import * as monacoEditor from 'monaco-editor';
import type { MonacoEditor } from '@guolao/vue-monaco-editor';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
import { useStyleStore } from '@/stores/style.store';
const props = withDefaults(defineProps<EditorProps>(), {
theme: 'vs',
options: () => ({}),
overrideServices: () => ({}),
saveViewState: true,
width: '100%',
height: '100%',
});
const emits = defineEmits<{
(e: 'update:value', value: string | undefined): void
(e: 'beforeMount', monaco: MonacoEditor): void
(e: 'mount', editor: monacoEditor.editor.IStandaloneCodeEditor, monaco: MonacoEditor): void
(e: 'change', value: string | undefined, event: monacoEditor.editor.IModelContentChangedEvent): void
(e: 'validate', markers: monacoEditor.editor.IMarker[]): void
}>();
interface MonacoEnvironment {
getWorker(_: any, label: string): Worker
}
// eslint-disable-next-line @typescript-eslint/no-namespace
declare module globalThis {
let MonacoEnvironment: MonacoEnvironment;
}
const value = useVModel(props, 'value', emits);
globalThis.MonacoEnvironment = {
getWorker(_: any, label: string) {
if (label === 'json') {
return new JsonWorker();
}
if (label === 'css' || label === 'scss' || label === 'less') {
return new CssWorker();
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return new HtmlWorker();
}
if (label === 'typescript' || label === 'javascript') {
return new TsWorker();
}
return new EditorWorker();
},
};
export interface EditorProps {
defaultValue?: string
defaultPath?: string
defaultLanguage?: string
value?: string
language?: string
path?: string
/* === */
theme: 'vs' | string
line?: number
options?: monacoEditor.editor.IStandaloneEditorConstructionOptions
overrideServices?: monacoEditor.editor.IEditorOverrideServices
saveViewState?: boolean
/* === */
width?: number | string
height?: number | string
className?: string
}
monacoEditor.editor.defineTheme('it-tools-dark', {
base: 'vs-dark',
inherit: true,
rules: [],
colors: {
'editor.background': '#00000000',
},
});
monacoEditor.editor.defineTheme('it-tools-light', {
base: 'vs',
inherit: true,
rules: [],
colors: {
'editor.background': '#00000000',
},
});
const styleStore = useStyleStore();
watch(
() => styleStore.isDarkTheme,
isDarkTheme => monacoEditor.editor.setTheme(isDarkTheme ? 'it-tools-dark' : 'it-tools-light'),
{ immediate: true },
);
const attrs = useAttrs();
const inheritedAttrs = { ...attrs, ...props };
</script>
<script lang="ts">
export default {
inheritAttrs: false,
};
</script>
<template>
<vue-monaco-editor
v-bind="inheritedAttrs"
v-model:value="value"
@before-mount="(monaco: MonacoEditor) => emits('beforeMount', monaco)"
@mount="(editor: monacoEditor.editor.IStandaloneCodeEditor, monaco: MonacoEditor) => emits('mount', editor, monaco)"
@change="(value: string | undefined, event: monacoEditor.editor.IModelContentChangedEvent) => emits('change', value, event)"
@validate="(markers: monacoEditor.editor.IMarker[]) => emits('validate', markers)"
/>
</template>