difit 3.0.1 → 3.0.3
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/dist/client/assets/index-BXrl8n3U.css +1 -0
- package/dist/client/assets/index-D8OwPIt3.js +29 -0
- package/dist/client/assets/{prism-csharp-eYFK1KLy.js → prism-csharp-MxIkBOuT.js} +1 -1
- package/dist/client/assets/{prism-hcl-CUXALSCA.js → prism-hcl-a0Z8Hwk2.js} +1 -1
- package/dist/client/assets/{prism-java-PGMVZJQD.js → prism-java-Blsgme8U.js} +1 -1
- package/dist/client/assets/{prism-php-DMdQhe-3.js → prism-php-CU6Zo_1W.js} +1 -1
- package/dist/client/assets/{prism-ruby-CuHQMYdx.js → prism-ruby-BU9Ro5cR.js} +1 -1
- package/dist/client/assets/{prism-solidity-Bba4k6Yw.js → prism-solidity-ChlixQd2.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/file-watcher.d.ts +5 -0
- package/dist/server/file-watcher.js +21 -2
- package/dist/server/file-watcher.test.js +52 -0
- package/dist/server/git-diff.d.ts +15 -0
- package/dist/server/git-diff.js +77 -0
- package/dist/server/server.js +68 -5
- package/dist/server/server.test.js +25 -0
- package/dist/types/diff.d.ts +22 -0
- package/package.json +2 -1
- package/dist/client/assets/index-BvuQgAey.css +0 -1
- package/dist/client/assets/index-DETp-4Z-.js +0 -29
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as U}from"./index-
|
|
1
|
+
import{g as U}from"./index-D8OwPIt3.js";function W(r,t){for(var e=0;e<t.length;e++){const a=t[e];if(typeof a!="string"&&!Array.isArray(a)){for(const s in a)if(s!=="default"&&!(s in r)){const u=Object.getOwnPropertyDescriptor(a,s);u&&Object.defineProperty(r,s,u.get?u:{enumerable:!0,get:()=>a[s]})}}}return Object.freeze(Object.defineProperty(r,Symbol.toStringTag,{value:"Module"}))}var I={},O;function Z(){return O||(O=1,(function(r){function t(n,l){return n.replace(/<<(\d+)>>/g,function(f,M){return"(?:"+l[+M]+")"})}function e(n,l,f){return RegExp(t(n,l),"")}function a(n,l){for(var f=0;f<l;f++)n=n.replace(/<<self>>/g,function(){return"(?:"+n+")"});return n.replace(/<<self>>/g,"[^\\s\\S]")}var s={type:"bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void",typeDeclaration:"class enum interface record struct",contextual:"add alias and ascending async await by descending from(?=\\s*(?:\\w|$)) get global group into init(?=\\s*;) join let nameof not notnull on or orderby partial remove select set unmanaged value when where with(?=\\s*{)",other:"abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield"};function u(n){return"\\b(?:"+n.trim().replace(/ /g,"|")+")\\b"}var v=u(s.typeDeclaration),d=RegExp(u(s.type+" "+s.typeDeclaration+" "+s.contextual+" "+s.other)),T=u(s.typeDeclaration+" "+s.contextual+" "+s.other),z=u(s.type+" "+s.typeDeclaration+" "+s.other),g=a(/<(?:[^<>;=+\-*/%&|^]|<<self>>)*>/.source,2),h=a(/\((?:[^()]|<<self>>)*\)/.source,2),i=/@?\b[A-Za-z_]\w*\b/.source,b=t(/<<0>>(?:\s*<<1>>)?/.source,[i,g]),c=t(/(?!<<0>>)<<1>>(?:\s*\.\s*<<1>>)*/.source,[T,b]),y=/\[\s*(?:,\s*)*\]/.source,A=t(/<<0>>(?:\s*(?:\?\s*)?<<1>>)*(?:\s*\?)?/.source,[c,y]),K=t(/[^,()<>[\];=+\-*/%&|^]|<<0>>|<<1>>|<<2>>/.source,[g,h,y]),q=t(/\(<<0>>+(?:,<<0>>+)+\)/.source,[K]),p=t(/(?:<<0>>|<<1>>)(?:\s*(?:\?\s*)?<<2>>)*(?:\s*\?)?/.source,[q,c,y]),o={keyword:d,punctuation:/[<>()?,.:[\]]/},k=/'(?:[^\r\n'\\]|\\.|\\[Uux][\da-fA-F]{1,8})'/.source,w=/"(?:\\.|[^\\"\r\n])*"/.source,F=/@"(?:""|\\[\s\S]|[^\\"])*"(?!")/.source;r.languages.csharp=r.languages.extend("clike",{string:[{pattern:e(/(^|[^$\\])<<0>>/.source,[F]),lookbehind:!0,greedy:!0},{pattern:e(/(^|[^@$\\])<<0>>/.source,[w]),lookbehind:!0,greedy:!0}],"class-name":[{pattern:e(/(\busing\s+static\s+)<<0>>(?=\s*;)/.source,[c]),lookbehind:!0,inside:o},{pattern:e(/(\busing\s+<<0>>\s*=\s*)<<1>>(?=\s*;)/.source,[i,p]),lookbehind:!0,inside:o},{pattern:e(/(\busing\s+)<<0>>(?=\s*=)/.source,[i]),lookbehind:!0},{pattern:e(/(\b<<0>>\s+)<<1>>/.source,[v,b]),lookbehind:!0,inside:o},{pattern:e(/(\bcatch\s*\(\s*)<<0>>/.source,[c]),lookbehind:!0,inside:o},{pattern:e(/(\bwhere\s+)<<0>>/.source,[i]),lookbehind:!0},{pattern:e(/(\b(?:is(?:\s+not)?|as)\s+)<<0>>/.source,[A]),lookbehind:!0,inside:o},{pattern:e(/\b<<0>>(?=\s+(?!<<1>>|with\s*\{)<<2>>(?:\s*[=,;:{)\]]|\s+(?:in|when)\b))/.source,[p,z,i]),inside:o}],keyword:d,number:/(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:[dflmu]|lu|ul)?\b/i,operator:/>>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/,punctuation:/\?\.?|::|[{}[\];(),.:]/}),r.languages.insertBefore("csharp","number",{range:{pattern:/\.\./,alias:"operator"}}),r.languages.insertBefore("csharp","punctuation",{"named-parameter":{pattern:e(/([(,]\s*)<<0>>(?=\s*:)/.source,[i]),lookbehind:!0,alias:"punctuation"}}),r.languages.insertBefore("csharp","class-name",{namespace:{pattern:e(/(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source,[i]),lookbehind:!0,inside:{punctuation:/\./}},"type-expression":{pattern:e(/(\b(?:default|sizeof|typeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/.source,[h]),lookbehind:!0,alias:"class-name",inside:o},"return-type":{pattern:e(/<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source,[p,c]),inside:o,alias:"class-name"},"constructor-invocation":{pattern:e(/(\bnew\s+)<<0>>(?=\s*[[({])/.source,[p]),lookbehind:!0,inside:o,alias:"class-name"},"generic-method":{pattern:e(/<<0>>\s*<<1>>(?=\s*\()/.source,[i,g]),inside:{function:e(/^<<0>>/.source,[i]),generic:{pattern:RegExp(g),alias:"class-name",inside:o}}},"type-list":{pattern:e(/\b((?:<<0>>\s+<<1>>|record\s+<<1>>\s*<<5>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>|<<1>>\s*<<5>>|<<6>>)(?:\s*,\s*(?:<<3>>|<<4>>|<<6>>))*(?=\s*(?:where|[{;]|=>|$))/.source,[v,b,i,p,d.source,h,/\bnew\s*\(\s*\)/.source]),lookbehind:!0,inside:{"record-arguments":{pattern:e(/(^(?!new\s*\()<<0>>\s*)<<1>>/.source,[b,h]),lookbehind:!0,greedy:!0,inside:r.languages.csharp},keyword:d,"class-name":{pattern:RegExp(p),greedy:!0,inside:o},punctuation:/[,()]/}},preprocessor:{pattern:/(^[\t ]*)#.*/m,lookbehind:!0,alias:"property",inside:{directive:{pattern:/(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/,lookbehind:!0,alias:"keyword"}}}});var x=w+"|"+k,E=t(/\/(?![*/])|\/\/[^\r\n]*[\r\n]|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>/.source,[x]),S=a(t(/[^"'/()]|<<0>>|\(<<self>>*\)/.source,[E]),2),_=/\b(?:assembly|event|field|method|module|param|property|return|type)\b/.source,N=t(/<<0>>(?:\s*\(<<1>>*\))?/.source,[c,S]);r.languages.insertBefore("csharp","class-name",{attribute:{pattern:e(/((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/.source,[_,N]),lookbehind:!0,greedy:!0,inside:{target:{pattern:e(/^<<0>>(?=\s*:)/.source,[_]),alias:"keyword"},"attribute-arguments":{pattern:e(/\(<<0>>*\)/.source,[S]),inside:r.languages.csharp},"class-name":{pattern:RegExp(c),inside:{punctuation:/\./}},punctuation:/[:,]/}}});var m=/:[^}\r\n]+/.source,C=a(t(/[^"'/()]|<<0>>|\(<<self>>*\)/.source,[E]),2),$=t(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[C,m]),R=a(t(/[^"'/()]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>|\(<<self>>*\)/.source,[x]),2),D=t(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[R,m]);function j(n,l){return{interpolation:{pattern:e(/((?:^|[^{])(?:\{\{)*)<<0>>/.source,[n]),lookbehind:!0,inside:{"format-string":{pattern:e(/(^\{(?:(?![}:])<<0>>)*)<<1>>(?=\}$)/.source,[l,m]),lookbehind:!0,inside:{punctuation:/^:/}},punctuation:/^\{|\}$/,expression:{pattern:/[\s\S]+/,alias:"language-csharp",inside:r.languages.csharp}}},string:/[\s\S]+/}}r.languages.insertBefore("csharp","string",{"interpolation-string":[{pattern:e(/(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source,[$]),lookbehind:!0,greedy:!0,inside:j($,C)},{pattern:e(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source,[D]),lookbehind:!0,greedy:!0,inside:j(D,R)}],char:{pattern:RegExp(k),greedy:!0}}),r.languages.dotnet=r.languages.cs=r.languages.csharp})(Prism)),I}var B=Z();const G=U(B),J=W({__proto__:null,default:G},[B]);export{J as p};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as u}from"./index-
|
|
1
|
+
import{g as u}from"./index-D8OwPIt3.js";function d(t,o){for(var a=0;a<o.length;a++){const e=o[a];if(typeof e!="string"&&!Array.isArray(e)){for(const r in e)if(r!=="default"&&!(r in t)){const i=Object.getOwnPropertyDescriptor(e,r);i&&Object.defineProperty(t,r,i.get?i:{enumerable:!0,get:()=>e[r]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}var s={},n;function p(){return n||(n=1,Prism.languages.hcl={comment:/(?:\/\/|#).*|\/\*[\s\S]*?(?:\*\/|$)/,heredoc:{pattern:/<<-?(\w+\b)[\s\S]*?^[ \t]*\1/m,greedy:!0,alias:"string"},keyword:[{pattern:/(?:data|resource)\s+(?:"(?:\\[\s\S]|[^\\"])*")(?=\s+"[\w-]+"\s+\{)/i,inside:{type:{pattern:/(resource|data|\s+)(?:"(?:\\[\s\S]|[^\\"])*")/i,lookbehind:!0,alias:"variable"}}},{pattern:/(?:backend|module|output|provider|provisioner|variable)\s+(?:[\w-]+|"(?:\\[\s\S]|[^\\"])*")\s+(?=\{)/i,inside:{type:{pattern:/(backend|module|output|provider|provisioner|variable)\s+(?:[\w-]+|"(?:\\[\s\S]|[^\\"])*")\s+/i,lookbehind:!0,alias:"variable"}}},/[\w-]+(?=\s+\{)/],property:[/[-\w\.]+(?=\s*=(?!=))/,/"(?:\\[\s\S]|[^\\"])+"(?=\s*[:=])/],string:{pattern:/"(?:[^\\$"]|\\[\s\S]|\$(?:(?=")|\$+(?!\$)|[^"${])|\$\{(?:[^{}"]|"(?:[^\\"]|\\[\s\S])*")*\})*"/,greedy:!0,inside:{interpolation:{pattern:/(^|[^$])\$\{(?:[^{}"]|"(?:[^\\"]|\\[\s\S])*")*\}/,lookbehind:!0,inside:{type:{pattern:/(\b(?:count|data|local|module|path|self|terraform|var)\b\.)[\w\*]+/i,lookbehind:!0,alias:"variable"},keyword:/\b(?:count|data|local|module|path|self|terraform|var)\b/i,function:/\w+(?=\()/,string:{pattern:/"(?:\\[\s\S]|[^\\"])*"/,greedy:!0},number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i,punctuation:/[!\$#%&'()*+,.\/;<=>@\[\\\]^`{|}~?:]/}}}},number:/\b0x[\da-f]+\b|\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i,boolean:/\b(?:false|true)\b/i,punctuation:/[=\[\]{}]/}),s}var l=p();const b=u(l),f=d({__proto__:null,default:b},[l]);export{f as p};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as u}from"./index-
|
|
1
|
+
import{g as u}from"./index-D8OwPIt3.js";function p(t,a){for(var r=0;r<a.length;r++){const e=a[r];if(typeof e!="string"&&!Array.isArray(e)){for(const s in e)if(s!=="default"&&!(s in t)){const n=Object.getOwnPropertyDescriptor(e,s);n&&Object.defineProperty(t,s,n.get?n:{enumerable:!0,get:()=>e[s]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}var o={},i;function d(){return i||(i=1,(function(t){var a=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\s*[(){}[\]<>=%~.:,;?+\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,r=/(?:[a-z]\w*\s*\.\s*)*(?:[A-Z]\w*\s*\.\s*)*/.source,e={pattern:RegExp(/(^|[^\w.])/.source+r+/[A-Z](?:[\d_A-Z]*[a-z]\w*)?\b/.source),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}};t.languages.java=t.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"/,lookbehind:!0,greedy:!0},"class-name":[e,{pattern:RegExp(/(^|[^\w.])/.source+r+/[A-Z]\w*(?=\s+\w+\s*[;,=()]|\s*(?:\[[\s,]*\]\s*)?::\s*new\b)/.source),lookbehind:!0,inside:e.inside},{pattern:RegExp(/(\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\s+)/.source+r+/[A-Z]\w*\b/.source),lookbehind:!0,inside:e.inside}],keyword:a,function:[t.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0},constant:/\b[A-Z][A-Z_\d]+\b/}),t.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"},char:{pattern:/'(?:\\.|[^'\\\r\n]){1,6}'/,greedy:!0}}),t.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":e,keyword:a,punctuation:/[<>(),.:]/,operator:/[?&|]/}},import:[{pattern:RegExp(/(\bimport\s+)/.source+r+/(?:[A-Z]\w*|\*)(?=\s*;)/.source),lookbehind:!0,inside:{namespace:e.inside.namespace,punctuation:/\./,operator:/\*/,"class-name":/\w+/}},{pattern:RegExp(/(\bimport\s+static\s+)/.source+r+/(?:\w+|\*)(?=\s*;)/.source),lookbehind:!0,alias:"static",inside:{namespace:e.inside.namespace,static:/\b\w+$/,punctuation:/\./,operator:/\*/,"class-name":/\w+/}}],namespace:{pattern:RegExp(/(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)(?!<keyword>)[a-z]\w*(?:\.[a-z]\w*)*\.?/.source.replace(/<keyword>/g,function(){return a.source})),lookbehind:!0,inside:{punctuation:/\./}}})})(Prism)),o}var c=d();const l=u(c),w=p({__proto__:null,default:l},[c]);export{w as p};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as c}from"./index-
|
|
1
|
+
import{g as c}from"./index-D8OwPIt3.js";function f(e,r){for(var n=0;n<r.length;n++){const t=r[n];if(typeof t!="string"&&!Array.isArray(t)){for(const a in t)if(a!=="default"&&!(a in e)){const i=Object.getOwnPropertyDescriptor(t,a);i&&Object.defineProperty(e,a,i.get?i:{enumerable:!0,get:()=>t[a]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}var u={},d;function g(){return d||(d=1,(function(e){var r=/\/\*[\s\S]*?\*\/|\/\/.*|#(?!\[).*/,n=[{pattern:/\b(?:false|true)\b/i,alias:"boolean"},{pattern:/(::\s*)\b[a-z_]\w*\b(?!\s*\()/i,greedy:!0,lookbehind:!0},{pattern:/(\b(?:case|const)\s+)\b[a-z_]\w*(?=\s*[;=])/i,greedy:!0,lookbehind:!0},/\b(?:null)\b/i,/\b[A-Z_][A-Z0-9_]*\b(?!\s*\()/],t=/\b0b[01]+(?:_[01]+)*\b|\b0o[0-7]+(?:_[0-7]+)*\b|\b0x[\da-f]+(?:_[\da-f]+)*\b|(?:\b\d+(?:_\d+)*\.?(?:\d+(?:_\d+)*)?|\B\.\d+)(?:e[+-]?\d+)?/i,a=/<?=>|\?\?=?|\.{3}|\??->|[!=]=?=?|::|\*\*=?|--|\+\+|&&|\|\||<<|>>|[?~]|[/^|%*&<>.+-]=?/,i=/[{}\[\](),:;]/;e.languages.php={delimiter:{pattern:/\?>$|^<\?(?:php(?=\s)|=)?/i,alias:"important"},comment:r,variable:/\$+(?:\w+\b|(?=\{))/,package:{pattern:/(namespace\s+|use\s+(?:function\s+)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,lookbehind:!0,inside:{punctuation:/\\/}},"class-name-definition":{pattern:/(\b(?:class|enum|interface|trait)\s+)\b[a-z_]\w*(?!\\)\b/i,lookbehind:!0,alias:"class-name"},"function-definition":{pattern:/(\bfunction\s+)[a-z_]\w*(?=\s*\()/i,lookbehind:!0,alias:"function"},keyword:[{pattern:/(\(\s*)\b(?:array|bool|boolean|float|int|integer|object|string)\b(?=\s*\))/i,alias:"type-casting",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|object|self|static|string)\b(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|never|object|self|static|string|void)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/\b(?:array(?!\s*\()|bool|float|int|iterable|mixed|object|string|void)\b/i,alias:"type-declaration",greedy:!0},{pattern:/(\|\s*)(?:false|null)\b|\b(?:false|null)(?=\s*\|)/i,alias:"type-declaration",greedy:!0,lookbehind:!0},{pattern:/\b(?:parent|self|static)(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(\byield\s+)from\b/i,lookbehind:!0},/\bclass\b/i,{pattern:/((?:^|[^\s>:]|(?:^|[^-])>|(?:^|[^:]):)\s*)\b(?:abstract|and|array|as|break|callable|case|catch|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|match|namespace|never|new|or|parent|print|private|protected|public|readonly|require|require_once|return|self|static|switch|throw|trait|try|unset|use|var|while|xor|yield|__halt_compiler)\b/i,lookbehind:!0}],"argument-name":{pattern:/([(,]\s*)\b[a-z_]\w*(?=\s*:(?!:))/i,lookbehind:!0},"class-name":[{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self|\s+static))\s+|\bcatch\s*\()\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/(\|\s*)\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/\b[a-z_]\w*(?!\\)\b(?=\s*\|)/i,greedy:!0},{pattern:/(\|\s*)(?:\\?\b[a-z_]\w*)+\b/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(?:\\?\b[a-z_]\w*)+\b(?=\s*\|)/i,alias:"class-name-fully-qualified",greedy:!0,inside:{punctuation:/\\/}},{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self\b|\s+static\b))\s+|\bcatch\s*\()(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*\$)/i,alias:"type-declaration",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-declaration"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*::)/i,alias:["class-name-fully-qualified","static-context"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/([(,?]\s*)[a-z_]\w*(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-hint"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b[a-z_]\w*(?!\\)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:["class-name-fully-qualified","return-type"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:n,function:{pattern:/(^|[^\\\w])\\?[a-z_](?:[\w\\]*\w)?(?=\s*\()/i,lookbehind:!0,inside:{punctuation:/\\/}},property:{pattern:/(->\s*)\w+/,lookbehind:!0},number:t,operator:a,punctuation:i};var l={pattern:/\{\$(?:\{(?:\{[^{}]+\}|[^{}]+)\}|[^{}])+\}|(^|[^\\{])\$+(?:\w+(?:\[[^\r\n\[\]]+\]|->\w+)?)/,lookbehind:!0,inside:e.languages.php},o=[{pattern:/<<<'([^']+)'[\r\n](?:.*[\r\n])*?\1;/,alias:"nowdoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},{pattern:/<<<(?:"([^"]+)"[\r\n](?:.*[\r\n])*?\1;|([a-z_]\w*)[\r\n](?:.*[\r\n])*?\2;)/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:l}},{pattern:/`(?:\\[\s\S]|[^\\`])*`/,alias:"backtick-quoted-string",greedy:!0},{pattern:/'(?:\\[\s\S]|[^\\'])*'/,alias:"single-quoted-string",greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,alias:"double-quoted-string",greedy:!0,inside:{interpolation:l}}];e.languages.insertBefore("php","variable",{string:o,attribute:{pattern:/#\[(?:[^"'\/#]|\/(?![*/])|\/\/.*$|#(?!\[).*$|\/\*(?:[^*]|\*(?!\/))*\*\/|"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*')+\](?=\s*[a-z$#])/im,greedy:!0,inside:{"attribute-content":{pattern:/^(#\[)[\s\S]+(?=\]$)/,lookbehind:!0,inside:{comment:r,string:o,"attribute-class-name":[{pattern:/([^:]|^)\b[a-z_]\w*(?!\\)\b/i,alias:"class-name",greedy:!0,lookbehind:!0},{pattern:/([^:]|^)(?:\\?\b[a-z_]\w*)+/i,alias:["class-name","class-name-fully-qualified"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:n,number:t,operator:a,punctuation:i}},delimiter:{pattern:/^#\[|\]$/,alias:"punctuation"}}}}),e.hooks.add("before-tokenize",function(s){if(/<\?/.test(s.code)){var p=/<\?(?:[^"'/#]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#(?!\[))(?:[^?\n\r]|\?(?!>))*(?=$|\?>|[\r\n])|#\[|\/\*(?:[^*]|\*(?!\/))*(?:\*\/|$))*?(?:\?>|$)/g;e.languages["markup-templating"].buildPlaceholders(s,"php",p)}}),e.hooks.add("after-tokenize",function(s){e.languages["markup-templating"].tokenizePlaceholders(s,"php")})})(Prism)),u}var b=g();const y=c(b),m=f({__proto__:null,default:y},[b]);export{m as p};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as l}from"./index-
|
|
1
|
+
import{g as l}from"./index-D8OwPIt3.js";function d(e,r){for(var n=0;n<r.length;n++){const t=r[n];if(typeof t!="string"&&!Array.isArray(t)){for(const i in t)if(i!=="default"&&!(i in e)){const s=Object.getOwnPropertyDescriptor(t,i);s&&Object.defineProperty(e,i,s.get?s:{enumerable:!0,get:()=>t[i]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}var a={},o;function g(){return o||(o=1,(function(e){e.languages.ruby=e.languages.extend("clike",{comment:{pattern:/#.*|^=begin\s[\s\S]*?^=end/m,greedy:!0},"class-name":{pattern:/(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/,operator:/\.{2,3}|&\.|===|<?=>|[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/,punctuation:/[(){}[\].,;]/}),e.languages.insertBefore("ruby","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}});var r={pattern:/((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/,lookbehind:!0,inside:{content:{pattern:/^(#\{)[\s\S]+(?=\}$)/,lookbehind:!0,inside:e.languages.ruby},delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"}}};delete e.languages.ruby.function;var n="(?:"+[/([^a-zA-Z0-9\s{(\[<=])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source,/\((?:[^()\\]|\\[\s\S]|\((?:[^()\\]|\\[\s\S])*\))*\)/.source,/\{(?:[^{}\\]|\\[\s\S]|\{(?:[^{}\\]|\\[\s\S])*\})*\}/.source,/\[(?:[^\[\]\\]|\\[\s\S]|\[(?:[^\[\]\\]|\\[\s\S])*\])*\]/.source,/<(?:[^<>\\]|\\[\s\S]|<(?:[^<>\\]|\\[\s\S])*>)*>/.source].join("|")+")",t=/(?:"(?:\\.|[^"\\\r\n])*"|(?:\b[a-zA-Z_]\w*|[^\s\0-\x7F]+)[?!]?|\$.)/.source;e.languages.insertBefore("ruby","keyword",{"regex-literal":[{pattern:RegExp(/%r/.source+n+/[egimnosux]{0,6}/.source),greedy:!0,inside:{interpolation:r,regex:/[\s\S]+/}},{pattern:/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,lookbehind:!0,greedy:!0,inside:{interpolation:r,regex:/[\s\S]+/}}],variable:/[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,symbol:[{pattern:RegExp(/(^|[^:]):/.source+t),lookbehind:!0,greedy:!0},{pattern:RegExp(/([\r\n{(,][ \t]*)/.source+t+/(?=:(?!:))/.source),lookbehind:!0,greedy:!0}],"method-definition":{pattern:/(\bdef\s+)\w+(?:\s*\.\s*\w+)?/,lookbehind:!0,inside:{function:/\b\w+$/,keyword:/^self\b/,"class-name":/^\w+/,punctuation:/\./}}}),e.languages.insertBefore("ruby","string",{"string-literal":[{pattern:RegExp(/%[qQiIwWs]?/.source+n),greedy:!0,inside:{interpolation:r,string:/[\s\S]+/}},{pattern:/("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,greedy:!0,inside:{interpolation:r,string:/[\s\S]+/}},{pattern:/<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?/}},interpolation:r,string:/[\s\S]+/}},{pattern:/<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?'|'$/}},string:/[\s\S]+/}}],"command-literal":[{pattern:RegExp(/%x/.source+n),greedy:!0,inside:{interpolation:r,command:{pattern:/[\s\S]+/,alias:"string"}}},{pattern:/`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/,greedy:!0,inside:{interpolation:r,command:{pattern:/[\s\S]+/,alias:"string"}}}]}),delete e.languages.ruby.string,e.languages.insertBefore("ruby","number",{builtin:/\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/,constant:/\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/}),e.languages.rb=e.languages.ruby})(Prism)),a}var u=g();const p=l(u),b=d({__proto__:null,default:p},[u]);export{b as p};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as u}from"./index-
|
|
1
|
+
import{g as u}from"./index-D8OwPIt3.js";function d(t,n){for(var i=0;i<n.length;i++){const e=n[i];if(typeof e!="string"&&!Array.isArray(e)){for(const r in e)if(r!=="default"&&!(r in t)){const s=Object.getOwnPropertyDescriptor(e,r);s&&Object.defineProperty(t,r,s.get?s:{enumerable:!0,get:()=>e[r]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}var o={},a;function c(){return a||(a=1,Prism.languages.solidity=Prism.languages.extend("clike",{"class-name":{pattern:/(\b(?:contract|enum|interface|library|new|struct|using)\s+)(?!\d)[\w$]+/,lookbehind:!0},keyword:/\b(?:_|anonymous|as|assembly|assert|break|calldata|case|constant|constructor|continue|contract|default|delete|do|else|emit|enum|event|external|for|from|function|if|import|indexed|inherited|interface|internal|is|let|library|mapping|memory|modifier|new|payable|pragma|private|public|pure|require|returns?|revert|selfdestruct|solidity|storage|struct|suicide|switch|this|throw|using|var|view|while)\b/,operator:/=>|->|:=|=:|\*\*|\+\+|--|\|\||&&|<<=?|>>=?|[-+*/%^&|<>!=]=?|[~?]/}),Prism.languages.insertBefore("solidity","keyword",{builtin:/\b(?:address|bool|byte|u?int(?:8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?|string|bytes(?:[1-9]|[12]\d|3[0-2])?)\b/}),Prism.languages.insertBefore("solidity","number",{version:{pattern:/([<>]=?|\^)\d+\.\d+\.\d+\b/,lookbehind:!0,alias:"number"}}),Prism.languages.sol=Prism.languages.solidity),o}var l=c();const m=u(l),y=d({__proto__:null,default:m},[l]);export{y as p};
|
package/dist/client/index.html
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
<link rel="icon" href="/favicon-white.svg" media="(prefers-color-scheme: dark)" />
|
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
9
9
|
<title>difit - Git Diff Viewer</title>
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-D8OwPIt3.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BXrl8n3U.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div id="root"></div>
|
|
@@ -9,6 +9,11 @@ export declare class FileWatcherService {
|
|
|
9
9
|
constructor();
|
|
10
10
|
start(diffMode: DiffMode, watchPath: string, debounceMs?: number, onCacheInvalidate?: () => void): Promise<void>;
|
|
11
11
|
private setupWatchers;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the actual git directory path, handling git worktrees.
|
|
14
|
+
* Uses git rev-parse --git-dir which correctly handles both normal repos and worktrees.
|
|
15
|
+
*/
|
|
16
|
+
private resolveGitDir;
|
|
12
17
|
private shouldIgnoreEvent;
|
|
13
18
|
private matchesPattern;
|
|
14
19
|
private isRelevantGitFile;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join } from 'path';
|
|
1
|
+
import { join, resolve } from 'path';
|
|
2
2
|
import { subscribe } from '@parcel/watcher';
|
|
3
3
|
import { simpleGit } from 'simple-git';
|
|
4
4
|
import { DiffMode } from '../types/watch.js';
|
|
@@ -60,7 +60,11 @@ export class FileWatcherService {
|
|
|
60
60
|
}
|
|
61
61
|
async setupWatchers(modeConfig, basePath) {
|
|
62
62
|
for (const watchPath of modeConfig.watchPaths) {
|
|
63
|
-
|
|
63
|
+
let fullPath = join(basePath, watchPath);
|
|
64
|
+
// Resolve git worktree path for .git directory
|
|
65
|
+
if (watchPath === '.git') {
|
|
66
|
+
fullPath = await this.resolveGitDir(basePath);
|
|
67
|
+
}
|
|
64
68
|
try {
|
|
65
69
|
const subscription = (await subscribe(fullPath, async (err, events) => {
|
|
66
70
|
if (err) {
|
|
@@ -101,6 +105,21 @@ export class FileWatcherService {
|
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Resolve the actual git directory path, handling git worktrees.
|
|
110
|
+
* Uses git rev-parse --git-dir which correctly handles both normal repos and worktrees.
|
|
111
|
+
*/
|
|
112
|
+
async resolveGitDir(basePath) {
|
|
113
|
+
try {
|
|
114
|
+
const git = simpleGit(basePath);
|
|
115
|
+
const gitDir = await git.revparse(['--git-dir']);
|
|
116
|
+
return resolve(basePath, gitDir.trim());
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Fallback to default .git path
|
|
120
|
+
return join(basePath, '.git');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
104
123
|
shouldIgnoreEvent(filePath, ignorePatterns) {
|
|
105
124
|
return ignorePatterns.some((pattern) => {
|
|
106
125
|
// Handle negation patterns (e.g., "!.git/index")
|
|
@@ -9,6 +9,7 @@ vi.mock('@parcel/watcher', () => ({
|
|
|
9
9
|
vi.mock('simple-git', () => ({
|
|
10
10
|
simpleGit: vi.fn(() => ({
|
|
11
11
|
checkIgnore: vi.fn(),
|
|
12
|
+
revparse: vi.fn().mockResolvedValue('.git'),
|
|
12
13
|
})),
|
|
13
14
|
}));
|
|
14
15
|
const { subscribe } = await import('@parcel/watcher');
|
|
@@ -202,6 +203,7 @@ describe('FileWatcherService', () => {
|
|
|
202
203
|
for (const { mode, expectedType } of modes) {
|
|
203
204
|
const mockGit = {
|
|
204
205
|
checkIgnore: vi.fn().mockRejectedValue(new Error('No ignored files')),
|
|
206
|
+
revparse: vi.fn().mockResolvedValue('.git'),
|
|
205
207
|
};
|
|
206
208
|
vi.mocked(simpleGit).mockReturnValue(mockGit);
|
|
207
209
|
await fileWatcher.start(mode, '/test/path', 300);
|
|
@@ -222,4 +224,54 @@ describe('FileWatcherService', () => {
|
|
|
222
224
|
}
|
|
223
225
|
});
|
|
224
226
|
});
|
|
227
|
+
describe('git worktree support', () => {
|
|
228
|
+
it('should use resolved git directory for normal repository', async () => {
|
|
229
|
+
const mockGit = {
|
|
230
|
+
checkIgnore: vi.fn(),
|
|
231
|
+
revparse: vi.fn().mockResolvedValue('.git'),
|
|
232
|
+
};
|
|
233
|
+
vi.mocked(simpleGit).mockReturnValue(mockGit);
|
|
234
|
+
await fileWatcher.start(DiffMode.DEFAULT, '/test/path', 300);
|
|
235
|
+
expect(mockGit.revparse).toHaveBeenCalledWith(['--git-dir']);
|
|
236
|
+
expect(subscribe).toHaveBeenCalledWith('/test/path/.git', expect.any(Function), {
|
|
237
|
+
ignore: ['.git/objects/**', '.git/refs/**', 'node_modules/**'],
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
it('should resolve worktree git directory path', async () => {
|
|
241
|
+
const mockGit = {
|
|
242
|
+
checkIgnore: vi.fn(),
|
|
243
|
+
revparse: vi.fn().mockResolvedValue('/main/repo/.git/worktrees/feature'),
|
|
244
|
+
};
|
|
245
|
+
vi.mocked(simpleGit).mockReturnValue(mockGit);
|
|
246
|
+
await fileWatcher.start(DiffMode.DEFAULT, '/worktree/path', 300);
|
|
247
|
+
expect(mockGit.revparse).toHaveBeenCalledWith(['--git-dir']);
|
|
248
|
+
expect(subscribe).toHaveBeenCalledWith('/main/repo/.git/worktrees/feature', expect.any(Function), {
|
|
249
|
+
ignore: ['.git/objects/**', '.git/refs/**', 'node_modules/**'],
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
it('should resolve relative git directory path', async () => {
|
|
253
|
+
const mockGit = {
|
|
254
|
+
checkIgnore: vi.fn(),
|
|
255
|
+
revparse: vi.fn().mockResolvedValue('../.git/worktrees/feature'),
|
|
256
|
+
};
|
|
257
|
+
vi.mocked(simpleGit).mockReturnValue(mockGit);
|
|
258
|
+
await fileWatcher.start(DiffMode.DEFAULT, '/repos/worktree', 300);
|
|
259
|
+
expect(mockGit.revparse).toHaveBeenCalledWith(['--git-dir']);
|
|
260
|
+
// path.resolve('/repos/worktree', '../.git/worktrees/feature') = '/repos/.git/worktrees/feature'
|
|
261
|
+
expect(subscribe).toHaveBeenCalledWith('/repos/.git/worktrees/feature', expect.any(Function), {
|
|
262
|
+
ignore: ['.git/objects/**', '.git/refs/**', 'node_modules/**'],
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
it('should fallback to default .git path on error', async () => {
|
|
266
|
+
const mockGit = {
|
|
267
|
+
checkIgnore: vi.fn(),
|
|
268
|
+
revparse: vi.fn().mockRejectedValue(new Error('Not a git repository')),
|
|
269
|
+
};
|
|
270
|
+
vi.mocked(simpleGit).mockReturnValue(mockGit);
|
|
271
|
+
await fileWatcher.start(DiffMode.DEFAULT, '/test/path', 300);
|
|
272
|
+
expect(subscribe).toHaveBeenCalledWith('/test/path/.git', expect.any(Function), {
|
|
273
|
+
ignore: ['.git/objects/**', '.git/refs/**', 'node_modules/**'],
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
});
|
|
225
277
|
});
|
|
@@ -15,4 +15,19 @@ export declare class GitDiffParser {
|
|
|
15
15
|
validateCommit(commitish: string): Promise<boolean>;
|
|
16
16
|
parseStdinDiff(diffContent: string): DiffResponse;
|
|
17
17
|
getBlobContent(filepath: string, ref: string): Promise<Buffer>;
|
|
18
|
+
resolveCommitish(commitish: string): Promise<string>;
|
|
19
|
+
getDefaultBranch(): Promise<string | null>;
|
|
20
|
+
getRevisionOptions(currentBase?: string, currentTarget?: string): Promise<{
|
|
21
|
+
branches: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
current: boolean;
|
|
24
|
+
}>;
|
|
25
|
+
commits: Array<{
|
|
26
|
+
hash: string;
|
|
27
|
+
shortHash: string;
|
|
28
|
+
message: string;
|
|
29
|
+
}>;
|
|
30
|
+
resolvedBase?: string;
|
|
31
|
+
resolvedTarget?: string;
|
|
32
|
+
}>;
|
|
18
33
|
}
|
package/dist/server/git-diff.js
CHANGED
|
@@ -427,4 +427,81 @@ export class GitDiffParser {
|
|
|
427
427
|
throw new Error(`Failed to get blob content for ${filepath} at ${ref}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
|
+
async resolveCommitish(commitish) {
|
|
431
|
+
const hash = await this.git.revparse([commitish]);
|
|
432
|
+
return hash.substring(0, 7);
|
|
433
|
+
}
|
|
434
|
+
async getDefaultBranch() {
|
|
435
|
+
try {
|
|
436
|
+
// Try to get the default branch from origin/HEAD
|
|
437
|
+
const result = await this.git.raw(['symbolic-ref', 'refs/remotes/origin/HEAD']);
|
|
438
|
+
// Result will be like "refs/remotes/origin/main\n"
|
|
439
|
+
const match = result.trim().match(/refs\/remotes\/origin\/(.+)/);
|
|
440
|
+
if (match) {
|
|
441
|
+
return match[1];
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
// If origin/HEAD is not set, fall back to common default branches
|
|
446
|
+
const commonDefaults = ['main', 'master'];
|
|
447
|
+
const branchResult = await this.git.branchLocal();
|
|
448
|
+
const branchNames = Object.keys(branchResult.branches);
|
|
449
|
+
for (const defaultName of commonDefaults) {
|
|
450
|
+
if (branchNames.includes(defaultName)) {
|
|
451
|
+
return defaultName;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
async getRevisionOptions(currentBase, currentTarget) {
|
|
458
|
+
const [branchResult, logResult, defaultBranch] = await Promise.all([
|
|
459
|
+
this.git.branchLocal(),
|
|
460
|
+
this.git.log({ maxCount: 20 }),
|
|
461
|
+
this.getDefaultBranch(),
|
|
462
|
+
]);
|
|
463
|
+
const branches = Object.entries(branchResult.branches).map(([name, data]) => ({
|
|
464
|
+
name,
|
|
465
|
+
current: data.current,
|
|
466
|
+
}));
|
|
467
|
+
// Sort branches: default branch first, then current branch, then alphabetically
|
|
468
|
+
branches.sort((a, b) => {
|
|
469
|
+
if (defaultBranch) {
|
|
470
|
+
if (a.name === defaultBranch)
|
|
471
|
+
return -1;
|
|
472
|
+
if (b.name === defaultBranch)
|
|
473
|
+
return 1;
|
|
474
|
+
}
|
|
475
|
+
if (a.current && !b.current)
|
|
476
|
+
return -1;
|
|
477
|
+
if (!a.current && b.current)
|
|
478
|
+
return 1;
|
|
479
|
+
return a.name.localeCompare(b.name);
|
|
480
|
+
});
|
|
481
|
+
const commits = logResult.all.map((commit) => ({
|
|
482
|
+
hash: commit.hash,
|
|
483
|
+
shortHash: commit.hash.substring(0, 7),
|
|
484
|
+
message: commit.message,
|
|
485
|
+
}));
|
|
486
|
+
// Resolve HEAD and HEAD^ to actual commit hashes if they're being used
|
|
487
|
+
let resolvedBase;
|
|
488
|
+
let resolvedTarget;
|
|
489
|
+
if (currentBase && !['working', 'staged', '.'].includes(currentBase)) {
|
|
490
|
+
try {
|
|
491
|
+
resolvedBase = await this.resolveCommitish(currentBase);
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
// If resolution fails, leave undefined
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (currentTarget && !['working', 'staged', '.'].includes(currentTarget)) {
|
|
498
|
+
try {
|
|
499
|
+
resolvedTarget = await this.resolveCommitish(currentTarget);
|
|
500
|
+
}
|
|
501
|
+
catch {
|
|
502
|
+
// If resolution fails, leave undefined
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return { branches, commits, resolvedBase, resolvedTarget };
|
|
506
|
+
}
|
|
430
507
|
}
|
package/dist/server/server.js
CHANGED
|
@@ -43,12 +43,22 @@ export async function startServer(options) {
|
|
|
43
43
|
const invalidateCache = () => {
|
|
44
44
|
diffDataCache = null;
|
|
45
45
|
};
|
|
46
|
+
// Track current revisions for cache invalidation
|
|
47
|
+
let currentBaseCommitish = options.baseCommitish ?? '';
|
|
48
|
+
let currentTargetCommitish = options.targetCommitish ?? '';
|
|
46
49
|
app.get('/api/diff', async (req, res) => {
|
|
47
50
|
const ignoreWhitespace = req.query.ignoreWhitespace === 'true';
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
const requestedBase = req.query.base || options.baseCommitish || '';
|
|
52
|
+
const requestedTarget = req.query.target || options.targetCommitish || '';
|
|
53
|
+
// Check if revisions or whitespace setting changed
|
|
54
|
+
const revisionsChanged = requestedBase !== currentBaseCommitish || requestedTarget !== currentTargetCommitish;
|
|
55
|
+
const whitespaceChanged = ignoreWhitespace !== currentIgnoreWhitespace;
|
|
56
|
+
// Regenerate diff data if cache is invalid or settings changed
|
|
57
|
+
if (!diffDataCache || ((revisionsChanged || whitespaceChanged) && !options.stdinDiff)) {
|
|
50
58
|
currentIgnoreWhitespace = ignoreWhitespace;
|
|
51
|
-
|
|
59
|
+
currentBaseCommitish = requestedBase;
|
|
60
|
+
currentTargetCommitish = requestedTarget;
|
|
61
|
+
diffDataCache = await parser.parseDiff(requestedTarget, requestedBase, ignoreWhitespace);
|
|
52
62
|
}
|
|
53
63
|
// Get repository identifier for storage isolation
|
|
54
64
|
// Uses repository path for simplicity and worktree support
|
|
@@ -61,16 +71,69 @@ export async function startServer(options) {
|
|
|
61
71
|
catch {
|
|
62
72
|
// If we can't get repository path, leave undefined
|
|
63
73
|
}
|
|
74
|
+
// Resolve symbolic refs like HEAD/HEAD^ to actual hashes for the UI
|
|
75
|
+
let resolvedBase = currentBaseCommitish || 'stdin';
|
|
76
|
+
let resolvedTarget = currentTargetCommitish || 'stdin';
|
|
77
|
+
if (!options.stdinDiff &&
|
|
78
|
+
currentBaseCommitish &&
|
|
79
|
+
!['working', 'staged', '.'].includes(currentBaseCommitish)) {
|
|
80
|
+
try {
|
|
81
|
+
resolvedBase = await parser.resolveCommitish(currentBaseCommitish);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// If resolution fails, keep original value
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!options.stdinDiff &&
|
|
88
|
+
currentTargetCommitish &&
|
|
89
|
+
!['working', 'staged', '.'].includes(currentTargetCommitish)) {
|
|
90
|
+
try {
|
|
91
|
+
resolvedTarget = await parser.resolveCommitish(currentTargetCommitish);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// If resolution fails, keep original value
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const requestedBaseCommitish = currentBaseCommitish || 'stdin';
|
|
98
|
+
const requestedTargetCommitish = currentTargetCommitish || 'stdin';
|
|
64
99
|
res.json({
|
|
65
100
|
...diffDataCache,
|
|
66
101
|
ignoreWhitespace,
|
|
67
102
|
mode: diffMode,
|
|
68
|
-
baseCommitish:
|
|
69
|
-
targetCommitish:
|
|
103
|
+
baseCommitish: resolvedBase,
|
|
104
|
+
targetCommitish: resolvedTarget,
|
|
105
|
+
requestedBaseCommitish,
|
|
106
|
+
requestedTargetCommitish,
|
|
70
107
|
clearComments: options.clearComments,
|
|
71
108
|
repositoryId,
|
|
72
109
|
});
|
|
73
110
|
});
|
|
111
|
+
// Get available revisions for revision selector
|
|
112
|
+
app.get('/api/revisions', async (_req, res) => {
|
|
113
|
+
if (options.stdinDiff) {
|
|
114
|
+
res.status(400).json({ error: 'Revision selection not available for stdin diff' });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const { branches, commits, resolvedBase, resolvedTarget } = await parser.getRevisionOptions(currentBaseCommitish, currentTargetCommitish);
|
|
119
|
+
const response = {
|
|
120
|
+
specialOptions: [
|
|
121
|
+
{ value: '.', label: 'All Uncommitted Changes' },
|
|
122
|
+
{ value: 'staged', label: 'Staging Area' },
|
|
123
|
+
{ value: 'working', label: 'Working Directory' },
|
|
124
|
+
],
|
|
125
|
+
branches,
|
|
126
|
+
commits,
|
|
127
|
+
resolvedBase,
|
|
128
|
+
resolvedTarget,
|
|
129
|
+
};
|
|
130
|
+
res.json(response);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error('Error fetching revisions:', error);
|
|
134
|
+
res.status(500).json({ error: 'Failed to fetch revisions' });
|
|
135
|
+
}
|
|
136
|
+
});
|
|
74
137
|
app.get(/^\/api\/blob\/(.*)$/, async (req, res) => {
|
|
75
138
|
try {
|
|
76
139
|
// If using stdin diff, blob content is not available
|
|
@@ -43,6 +43,12 @@ vi.mock('./git-diff.js', () => {
|
|
|
43
43
|
isEmpty: false,
|
|
44
44
|
});
|
|
45
45
|
getBlobContent = vi.fn().mockResolvedValue(Buffer.from('mock image data'));
|
|
46
|
+
getRevisionOptions = vi.fn().mockResolvedValue({
|
|
47
|
+
branches: [{ name: 'main', current: true }],
|
|
48
|
+
commits: [{ hash: 'abc1234', shortHash: 'abc1234', message: 'Test commit' }],
|
|
49
|
+
resolvedBase: 'abc1234',
|
|
50
|
+
resolvedTarget: 'def5678',
|
|
51
|
+
});
|
|
46
52
|
}
|
|
47
53
|
return { GitDiffParser: GitDiffParserMock };
|
|
48
54
|
});
|
|
@@ -396,6 +402,25 @@ describe('Server Integration Tests', () => {
|
|
|
396
402
|
expect(data).toHaveProperty('mode', 'inline');
|
|
397
403
|
});
|
|
398
404
|
});
|
|
405
|
+
describe('Revision options API', () => {
|
|
406
|
+
it('returns available revisions', async () => {
|
|
407
|
+
const result = await startServer({
|
|
408
|
+
targetCommitish: 'HEAD',
|
|
409
|
+
baseCommitish: 'HEAD^',
|
|
410
|
+
});
|
|
411
|
+
servers.push(result.server);
|
|
412
|
+
const response = await fetch(`http://localhost:${result.port}/api/revisions`);
|
|
413
|
+
const data = (await response.json());
|
|
414
|
+
expect(response.ok).toBe(true);
|
|
415
|
+
expect(data.specialOptions).toHaveLength(3);
|
|
416
|
+
expect(data.branches).toEqual([{ name: 'main', current: true }]);
|
|
417
|
+
expect(data.commits).toEqual([
|
|
418
|
+
{ hash: 'abc1234', shortHash: 'abc1234', message: 'Test commit' },
|
|
419
|
+
]);
|
|
420
|
+
expect(data.resolvedBase).toBe('abc1234');
|
|
421
|
+
expect(data.resolvedTarget).toBe('def5678');
|
|
422
|
+
});
|
|
423
|
+
});
|
|
399
424
|
describe('Error handling', () => {
|
|
400
425
|
it.skip('handles invalid commit gracefully', async () => {
|
|
401
426
|
// This test is skipped due to mocking complexity
|
package/dist/types/diff.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export interface DiffResponse {
|
|
|
41
41
|
mode?: DiffViewMode;
|
|
42
42
|
baseCommitish?: string;
|
|
43
43
|
targetCommitish?: string;
|
|
44
|
+
requestedBaseCommitish?: string;
|
|
45
|
+
requestedTargetCommitish?: string;
|
|
44
46
|
clearComments?: boolean;
|
|
45
47
|
repositoryId?: string;
|
|
46
48
|
}
|
|
@@ -91,3 +93,23 @@ export interface DiffContextStorage {
|
|
|
91
93
|
comments: DiffComment[];
|
|
92
94
|
viewedFiles: ViewedFileRecord[];
|
|
93
95
|
}
|
|
96
|
+
export interface RevisionOption {
|
|
97
|
+
value: string;
|
|
98
|
+
label: string;
|
|
99
|
+
}
|
|
100
|
+
export interface BranchInfo {
|
|
101
|
+
name: string;
|
|
102
|
+
current: boolean;
|
|
103
|
+
}
|
|
104
|
+
export interface CommitInfo {
|
|
105
|
+
hash: string;
|
|
106
|
+
shortHash: string;
|
|
107
|
+
message: string;
|
|
108
|
+
}
|
|
109
|
+
export interface RevisionsResponse {
|
|
110
|
+
specialOptions: RevisionOption[];
|
|
111
|
+
branches: BranchInfo[];
|
|
112
|
+
commits: CommitInfo[];
|
|
113
|
+
resolvedBase?: string;
|
|
114
|
+
resolvedTarget?: string;
|
|
115
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "difit",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "A lightweight command-line tool that spins up a local web server to display Git commit diffs in a GitHub-like Files changed view",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"perf:compare": "node scripts/compare-performance.js"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"@floating-ui/react": "^0.27.16",
|
|
43
44
|
"@octokit/rest": "^22.0.0",
|
|
44
45
|
"@parcel/watcher": "^2.5.1",
|
|
45
46
|
"commander": "^14.0.0",
|