From 1cf932d929f565e57cacce8a27f1fc692ee3014a Mon Sep 17 00:00:00 2001 From: Jacob Rienstra Date: Fri, 12 Jun 2020 09:50:35 -0400 Subject: [PATCH] Code interface save as JSON if possible (#702) * save as json * linters! --- package.json | 8 + src/interfaces/code/code.story.ts | 4 +- src/interfaces/code/code.vue | 68 +++++++- src/interfaces/code/index.ts | 2 + src/shims.d.ts | 16 ++ yarn.lock | 261 +++++++++++++++++++++++++++--- 6 files changed, 327 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index 81810e6de4..3c0aae00b8 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ "@tinymce/tinymce-vue": "^3.2.1", "@types/codemirror": "^0.0.96", "@types/debug": "^4.1.5", + "@types/htmlhint": "^0.9.1", + "@types/js-yaml": "^3.12.4", "@types/lodash": "^4.14.155", "@types/mousetrap": "^1.6.3", "@vue/composition-api": "^0.6.2", @@ -30,8 +32,14 @@ "bytes": "^3.1.0", "codemirror": "^5.54.0", "cropperjs": "^1.5.7", + "csslint": "^1.0.5", "date-fns": "^2.14.0", "diff": "^4.0.2", + "htmlhint": "^0.14.0", + "js-yaml": "^3.14.0", + "jshint": "^2.11.1", + "jsonlint": "^1.6.3", + "jsonlint-mod": "^1.7.5", "lodash": "^4.17.15", "marked": "^1.1.0", "micromustache": "^7.1.0", diff --git a/src/interfaces/code/code.story.ts b/src/interfaces/code/code.story.ts index 759bf64bbb..36403376de 100644 --- a/src/interfaces/code/code.story.ts +++ b/src/interfaces/code/code.story.ts @@ -9,7 +9,7 @@ import 'codemirror/mode/meta'; const choices = {} as Record; -CodeMirror.modeInfo.forEach((e) => (choices[e.name] = e.mode)); +CodeMirror.modeInfo.forEach((e) => (e.name === 'JSON' ? (choices[e.name] = 'JSON') : (choices[e.name] = e.mode))); export default { title: 'Interfaces / Code', @@ -25,7 +25,7 @@ export const basic = () => i18n, props: { lineNumber: { - default: boolean('Line Number', false), + default: boolean('Line Number', true), }, disabled: { default: boolean('Disabled', false), diff --git a/src/interfaces/code/code.vue b/src/interfaces/code/code.vue index 76a2949fcf..a8e4c42dc2 100644 --- a/src/interfaces/code/code.vue +++ b/src/interfaces/code/code.vue @@ -26,6 +26,7 @@ import 'codemirror/mode/meta'; import 'codemirror/addon/search/searchcursor.js'; import 'codemirror/addon/search/matchesonscrollbar.js'; import 'codemirror/addon/scroll/annotatescrollbar.js'; +import 'codemirror/addon/lint/lint.js'; import 'codemirror/addon/search/search.js'; import 'codemirror/addon/comment/comment.js'; @@ -41,7 +42,7 @@ export default defineComponent({ default: false, }, value: { - type: String, + type: [String, Object], default: null, }, altOptions: { @@ -54,11 +55,11 @@ export default defineComponent({ }, lineNumber: { type: Boolean, - default: null, + default: true, }, language: { type: String, - default: 'javascript', + default: 'json', }, }, setup(props, { emit }) { @@ -75,7 +76,11 @@ export default defineComponent({ await setLanguage(); codemirror.value.on('change', (cm) => { const content = cm.getValue(); - emit('input', content); + try { + emit('input', JSON.parse(content)); + } catch { + emit('input', content); + } }); } }); @@ -103,8 +108,55 @@ export default defineComponent({ async function setLanguage() { if (codemirror.value) { - await import(`codemirror/mode/${props.language}/${props.language}.js`); - codemirror.value.setOption('mode', props.language); + const lang = props.language.toLowerCase(); + if (lang === 'json') { + // @ts-ignore + await import('codemirror/mode/javascript/javascript.js'); + const jsonlint = (await import('jsonlint-mod')) as any; + codemirror.value.setOption('mode', { name: 'javascript', json: true }); + CodeMirror.registerHelper('lint', 'json', (text: string) => { + const found: {}[] = []; + const parser = jsonlint.parser; + parser.parseError = (str: string, hash: any) => { + const loc = hash.loc; + found.push({ + from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), + to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), + message: str, + }); + }; + if (text.length > 0) { + try { + jsonlint.parse(text); + } catch (e) { + console.error(e); + } + } + return found; + }); + } else if (lang === 'javascript' || lang === 'htmlmixed' || lang === 'css' || lang === 'yaml') { + let linter = lang; + if (lang === 'javascript') { + const jshint = await import('jshint'); + (window as any).JSHINT = jshint; + } else if (lang === 'htmlmixed') { + linter = 'html'; + const htmlhint = await import('htmlhint'); + (window as any).HTMLHint = htmlhint; + } else if (lang === 'css') { + const csslint = await import('csslint'); + (window as any).CSSLint = csslint; + } else if (lang === 'yaml') { + const jsyaml = await import('js-yaml'); + (window as any).jsyaml = jsyaml; + } + await import(`codemirror/mode/${lang}/${lang}.js`); + await import(`codemirror/addon/lint/${linter}-lint.js`); + codemirror.value.setOption('lint', CodeMirror.lint[linter]); + } else { + await import(`codemirror/mode/${lang}/${lang}.js`); + codemirror.value.setOption('mode', { name: lang }); + } } } @@ -161,6 +213,8 @@ export default defineComponent({ showCursorWhenSelecting: true, theme: 'default', extraKeys: { Ctrl: 'autocomplete' }, + lint: true, + gutters: ['CodeMirror-lint-markers'], }; const cmOptions = computed>(() => { @@ -219,6 +273,8 @@ export default defineComponent({