v-code-diff 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "v-code-diff",
3
3
  "type": "module",
4
- "version": "1.14.0",
4
+ "version": "1.15.0",
5
5
  "packageManager": "pnpm@11.2.2",
6
6
  "description": "A code diff viewer for Vue 2.6, Vue 2.7, and Vue 3",
7
7
  "license": "MIT",
@@ -56,7 +56,9 @@
56
56
  "postinstall": "node scripts/postinstall.cjs",
57
57
  "prepublishOnly": "npm run build",
58
58
  "release": "bumpp --commit --no-push --tag && npm publish",
59
- "test": "vitest run"
59
+ "test": "run-s test:unit test:types",
60
+ "test:types": "tsc --noEmit --skipLibCheck --module ESNext --moduleResolution node --target ES2020 tests/types.test-d.ts",
61
+ "test:unit": "vitest run"
60
62
  },
61
63
  "peerDependencies": {
62
64
  "@vue/composition-api": "^1.4.9",
package/src/CodeDiff.vue CHANGED
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, shallowRef, watch } from 'vue-demi'
3
+ import type { DiffChangeClickEvent } from './types'
3
4
  import { createSplitDiff, createUnifiedDiff } from './utils'
4
5
  import UnifiedViewer from './unified/UnifiedViewer.vue'
5
6
  import SplitViewer from './split/SplitViewer.vue'
@@ -23,6 +24,7 @@ interface Props {
23
24
  newFilename?: string
24
25
  hideHeader?: boolean
25
26
  hideStat?: boolean
27
+ hideNavigation?: boolean
26
28
  theme?: 'light' | 'dark'
27
29
  // Give a pattern to ignore matching lines eg: '(time|token)'
28
30
  ignoreMatchingLines?: string
@@ -41,12 +43,14 @@ const props = withDefaults(defineProps<Props>(), {
41
43
  newFilename: undefined,
42
44
  hideHeader: false,
43
45
  hideStat: false,
46
+ hideNavigation: false,
44
47
  theme: 'light',
45
48
  ignoreMatchingLines: undefined,
46
49
  })
47
50
 
48
51
  const emits = defineEmits<{
49
52
  (e: 'diff', diffResult: DiffResult): void
53
+ (e: 'change-click', payload: DiffChangeClickEvent): void
50
54
  }>()
51
55
 
52
56
  interface DiffResult {
@@ -132,7 +136,7 @@ watch(() => props, () => {
132
136
  <div class="info-left">{{ filename }}</div>
133
137
  <div class="info-left">{{ newFilename }}</div>
134
138
  </span>
135
- <span class="diff-commandbar">
139
+ <span v-if="!hideNavigation" class="diff-commandbar">
136
140
  <button class="command-item-button" title="Next Change" @click="goToNextDiff">
137
141
  <DownArrowIcon />
138
142
  </button>
@@ -152,7 +156,7 @@ watch(() => props, () => {
152
156
  <span class="info-left">{{ filename }}</span>
153
157
  <span class="info-right">
154
158
  <span style="margin-left: 20px;">{{ newFilename }}</span>
155
- <span class="diff-commandbar">
159
+ <span v-if="!hideNavigation" class="diff-commandbar">
156
160
  <button class="command-item-button" title="Next Change" @click="goToNextDiff">
157
161
  <DownArrowIcon />
158
162
  </button>
@@ -169,8 +173,8 @@ watch(() => props, () => {
169
173
  </span>
170
174
  </div>
171
175
  </div>
172
- <UnifiedViewer v-if="isUnifiedViewer" :diff-change="diffChange" :language="language" />
173
- <SplitViewer v-else :diff-change="diffChange" :language="language" />
176
+ <UnifiedViewer v-if="isUnifiedViewer" :diff-change="diffChange" :language="language" @change-click="emits('change-click', $event)" />
177
+ <SplitViewer v-else :diff-change="diffChange" :language="language" @change-click="emits('change-click', $event)" />
174
178
  </div>
175
179
  </template>
176
180
 
@@ -1,12 +1,15 @@
1
1
  <script setup lang="ts">
2
2
  import { DiffType } from '../types'
3
- import type { SplitLineChange } from '../types'
3
+ import type { DiffChangeClickEvent, DiffLine, SplitLineChange } from '../types'
4
4
 
5
5
  defineProps<{
6
6
  splitLine: SplitLineChange
7
7
  }>()
8
8
 
9
- const emit = defineEmits(['expand'])
9
+ const emit = defineEmits<{
10
+ (e: 'expand', line: SplitLineChange): void
11
+ (e: 'change-click', payload: DiffChangeClickEvent): void
12
+ }>()
10
13
 
11
14
  function getCodeMarker(type: DiffType) {
12
15
  if (type === DiffType.DELETE)
@@ -27,6 +30,20 @@ function onSplitLineMousedown(event: MouseEvent, side: 'left' | 'right') {
27
30
  for (const el of leftElements)
28
31
  el.classList.toggle('no-select', side === 'right')
29
32
  }
33
+
34
+ function onChangeClick(event: MouseEvent, line: DiffLine, side: 'old' | 'new') {
35
+ if (line.type !== DiffType.ADD && line.type !== DiffType.DELETE)
36
+ return
37
+
38
+ // Vue 2 DOM templates require kebab-case event names.
39
+ // eslint-disable-next-line vue/custom-event-name-casing
40
+ emit('change-click', {
41
+ side,
42
+ type: line.type,
43
+ lineNumber: line.num!,
44
+ event,
45
+ })
46
+ }
30
47
  </script>
31
48
 
32
49
  <template>
@@ -71,6 +88,7 @@ function onSplitLineMousedown(event: MouseEvent, side: 'left' | 'right') {
71
88
  'split-side-right': index === 1,
72
89
  }"
73
90
  @mousedown="onSplitLineMousedown($event, index === 0 ? 'left' : 'right')"
91
+ @click="onChangeClick($event, line, index === 0 ? 'old' : 'new')"
74
92
  >
75
93
  <span
76
94
  class="blob-code-inner blob-code-marker" :data-code-marker="getCodeMarker(line.type)"
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, shallowRef, watch } from 'vue-demi'
3
- import type { SplitLineChange, SplitViewerChange } from '../types'
3
+ import type { DiffChangeClickEvent, SplitLineChange, SplitViewerChange } from '../types'
4
4
  import { RENDER_BATCH_SIZE, highlightSplitLine } from '../utils'
5
5
  import SplitLine from './SplitLine.vue'
6
6
 
@@ -9,6 +9,10 @@ const props = defineProps<{
9
9
  language: string
10
10
  }>()
11
11
 
12
+ const emit = defineEmits<{
13
+ (e: 'change-click', payload: DiffChangeClickEvent): void
14
+ }>()
15
+
12
16
  const renderLimit = ref(RENDER_BATCH_SIZE)
13
17
  const visibleChanges = shallowRef(props.diffChange.changes.filter(line => !line.hide || line.hideIndex !== undefined))
14
18
  const renderedChanges = computed(() => visibleChanges.value.slice(0, renderLimit.value))
@@ -50,7 +54,13 @@ function loadMore() {
50
54
  <col>
51
55
  </colgroup>
52
56
  <tbody>
53
- <SplitLine v-for="(item, index) in renderedChanges" :key="index" :split-line="item" @expand="expandHandler" />
57
+ <SplitLine
58
+ v-for="(item, index) in renderedChanges"
59
+ :key="index"
60
+ :split-line="item"
61
+ @expand="expandHandler"
62
+ @change-click="emit('change-click', $event)"
63
+ />
54
64
  <tr v-if="remainingLines">
55
65
  <td class="blob-code blob-code-hunk load-more" colspan="4">
56
66
  <button class="load-more-button" type="button" @click="loadMore">
package/src/types.ts CHANGED
@@ -21,6 +21,13 @@ export interface DiffLine {
21
21
  num?: number
22
22
  }
23
23
 
24
+ export interface DiffChangeClickEvent {
25
+ side: 'old' | 'new'
26
+ type: DiffType.ADD | DiffType.DELETE
27
+ lineNumber: number
28
+ event: MouseEvent
29
+ }
30
+
24
31
  export interface SplitLineChange {
25
32
  fold?: boolean
26
33
  highlighted?: boolean
@@ -1,11 +1,28 @@
1
1
  <script lang="ts" setup>
2
- import type { UnifiedLineChange } from '../types'
2
+ import type { DiffChangeClickEvent, UnifiedLineChange } from '../types'
3
3
  import { DiffType } from '../types'
4
4
 
5
- defineProps<{
5
+ const props = defineProps<{
6
6
  line: UnifiedLineChange
7
7
  }>()
8
- const emit = defineEmits(['expand'])
8
+ const emit = defineEmits<{
9
+ (e: 'expand', line: UnifiedLineChange): void
10
+ (e: 'change-click', payload: DiffChangeClickEvent): void
11
+ }>()
12
+
13
+ function onChangeClick(event: MouseEvent) {
14
+ if (props.line.type !== DiffType.ADD && props.line.type !== DiffType.DELETE)
15
+ return
16
+
17
+ // Vue 2 DOM templates require kebab-case event names.
18
+ // eslint-disable-next-line vue/custom-event-name-casing
19
+ emit('change-click', {
20
+ side: props.line.type === DiffType.DELETE ? 'old' : 'new',
21
+ type: props.line.type,
22
+ lineNumber: props.line.type === DiffType.DELETE ? props.line.delNum! : props.line.addNum!,
23
+ event,
24
+ })
25
+ }
9
26
 
10
27
  function getCodeMarker(type: DiffType) {
11
28
  if (type === DiffType.DELETE)
@@ -25,7 +42,7 @@ function getCodeMarker(type: DiffType) {
25
42
 
26
43
  </td>
27
44
  </tr>
28
- <tr v-else-if="!line.hide" :data-diff-change="line.type !== DiffType.EQUAL ? '' : undefined">
45
+ <tr v-else-if="!line.hide" :data-diff-change="line.type !== DiffType.EQUAL ? '' : undefined" @click="onChangeClick">
29
46
  <td
30
47
  class="blob-num"
31
48
  :class="{
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { computed, ref, shallowRef, watch } from 'vue-demi'
3
- import type { UnifiedLineChange, UnifiedViewerChange } from '../types'
3
+ import type { DiffChangeClickEvent, UnifiedLineChange, UnifiedViewerChange } from '../types'
4
4
  import { RENDER_BATCH_SIZE, highlightUnifiedLine } from '../utils'
5
5
  import UnifiedLine from './UnifiedLine.vue'
6
6
 
@@ -9,6 +9,10 @@ const props = defineProps<{
9
9
  language: string
10
10
  }>()
11
11
 
12
+ const emit = defineEmits<{
13
+ (e: 'change-click', payload: DiffChangeClickEvent): void
14
+ }>()
15
+
12
16
  const renderLimit = ref(RENDER_BATCH_SIZE)
13
17
  const visibleChanges = shallowRef(props.diffChange.changes.filter(line => !line.hide || line.hideIndex !== undefined))
14
18
  const renderedChanges = computed(() => visibleChanges.value.slice(0, renderLimit.value))
@@ -44,7 +48,13 @@ function loadMore() {
44
48
  <template>
45
49
  <table class="diff-table">
46
50
  <tbody>
47
- <UnifiedLine v-for="(item, index) in renderedChanges" :key="index" :line="item" @expand="expandHandler" />
51
+ <UnifiedLine
52
+ v-for="(item, index) in renderedChanges"
53
+ :key="index"
54
+ :line="item"
55
+ @expand="expandHandler"
56
+ @change-click="emit('change-click', $event)"
57
+ />
48
58
  <tr v-if="remainingLines">
49
59
  <td class="blob-code blob-code-hunk load-more" colspan="3">
50
60
  <button class="load-more-button" type="button" @click="loadMore">
package/src/utils.ts CHANGED
@@ -74,6 +74,11 @@ function diffLines(prev: string, current: string) {
74
74
  current = current.slice(0, -1)
75
75
  }
76
76
 
77
+ if (prev)
78
+ prev += '\n'
79
+ if (current)
80
+ current += '\n'
81
+
77
82
  let changes: Diff.Change[] | undefined
78
83
  if (prev.length + current.length >= 100_000 && prev.includes('\n') && current.includes('\n')) {
79
84
  const prevLines = prev ? prev.replace(/\n$/, '').split('\n') : []
package/types/index.d.ts CHANGED
@@ -1,18 +1,75 @@
1
1
  import type { HLJSApi } from 'highlight.js'
2
- import type { DefineComponent } from 'vue-demi'
2
+ import type { DefineComponent, VNode } from 'vue-demi'
3
3
 
4
- declare const _default: {
5
- install: (app: any) => void
6
- hljs: HLJSApi
4
+ export interface CodeDiffProps {
5
+ newString: string
6
+ oldString: string
7
+ language?: string
8
+ context?: number
9
+ diffStyle?: 'word' | 'char'
10
+ forceInlineComparison?: boolean
11
+ outputFormat?: 'line-by-line' | 'side-by-side'
12
+ trim?: boolean
13
+ noDiffLineFeed?: boolean
14
+ maxHeight?: string
15
+ filename?: string
16
+ newFilename?: string
17
+ hideHeader?: boolean
18
+ hideStat?: boolean
19
+ hideNavigation?: boolean
20
+ theme?: 'light' | 'dark'
21
+ ignoreMatchingLines?: string
22
+ }
23
+
24
+ export interface CodeReaderProps {
25
+ text: string
26
+ language?: string
27
+ }
28
+
29
+ export interface DiffStat {
30
+ additionsNum: number
31
+ deletionsNum: number
32
+ ignoreAdditionsNum: number
33
+ ignoreDeletionsNum: number
34
+ }
35
+
36
+ export interface DiffResult {
37
+ stat: {
38
+ isChanged: boolean
39
+ addNum: number
40
+ delNum: number
41
+ }
42
+ }
43
+
44
+ export interface DiffChangeClickEvent {
45
+ side: 'old' | 'new'
46
+ type: 'added' | 'removed'
47
+ lineNumber: number
48
+ event: MouseEvent
7
49
  }
8
50
 
9
- // eslint-disable-next-line
10
- declare const CodeDiff: DefineComponent<{}, {}, any>
11
- // eslint-disable-next-line
12
- declare const CodeReader: DefineComponent<{}, {}, any>
13
- export {
14
- CodeDiff,
15
- CodeReader,
51
+ export interface CodeDiffSlots {
52
+ stat: (props: { stat: DiffStat }) => VNode[]
53
+ }
54
+
55
+ interface CodeDiffEmits {
56
+ diff: (diffResult: DiffResult) => void
57
+ 'change-click': (payload: DiffChangeClickEvent) => void
58
+ }
59
+
60
+ type Empty = Record<string, never>
61
+ type CodeDiffComponent = DefineComponent<CodeDiffProps, Empty, Empty, Empty, Empty, Empty, Empty, CodeDiffEmits, 'diff' | 'change-click'>
62
+
63
+ export declare const CodeDiff: CodeDiffComponent & {
64
+ new (): InstanceType<CodeDiffComponent> & { $slots: CodeDiffSlots }
65
+ }
66
+
67
+ export declare const CodeReader: DefineComponent<CodeReaderProps>
68
+ export declare const hljs: HLJSApi
69
+
70
+ declare const plugin: {
71
+ install: (app: { component: (name: string, component: unknown) => unknown }) => void
72
+ hljs: HLJSApi
16
73
  }
17
74
 
18
- export default _default
75
+ export default plugin