jostraca 0.38.0 → 0.39.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/dist/build/BuildContext.js +3 -1
- package/dist/build/BuildContext.js.map +1 -1
- package/dist/build/BuildMeta.js +17 -6
- package/dist/build/BuildMeta.js.map +1 -1
- package/dist/build/FileHandler.d.ts +7 -1
- package/dist/build/FileHandler.js +130 -71
- package/dist/build/FileHandler.js.map +1 -1
- package/dist/check.d.ts +1 -1
- package/dist/check.js +7 -4
- package/dist/check.js.map +1 -1
- package/dist/cmp/CopyFiles.d.ts +2 -1
- package/dist/cmp/CopyFiles.js +7 -3
- package/dist/cmp/CopyFiles.js.map +1 -1
- package/dist/cmp/Fragment.d.ts +2 -1
- package/dist/cmp/Fragment.js +75 -6
- package/dist/cmp/Fragment.js.map +1 -1
- package/dist/diff.js +34 -18
- package/dist/diff.js.map +1 -1
- package/dist/jostraca.d.ts +10 -10
- package/dist/jostraca.js +59 -32
- package/dist/jostraca.js.map +1 -1
- package/dist/op/CopyOp.js +15 -4
- package/dist/op/CopyOp.js.map +1 -1
- package/dist/op/FileOp.js +14 -2
- package/dist/op/FileOp.js.map +1 -1
- package/dist/op/FolderOp.js +3 -2
- package/dist/op/FolderOp.js.map +1 -1
- package/dist/op/FragmentOp.js +2 -0
- package/dist/op/FragmentOp.js.map +1 -1
- package/dist/op/InjectOp.js +18 -5
- package/dist/op/InjectOp.js.map +1 -1
- package/dist/op/ProjectOp.d.ts +1 -1
- package/dist/op/ProjectOp.js +22 -4
- package/dist/op/ProjectOp.js.map +1 -1
- package/dist/op/SlotOp.js +2 -0
- package/dist/op/SlotOp.js.map +1 -1
- package/dist/tree.d.ts +2 -1
- package/dist/tree.js +21 -1
- package/dist/tree.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/util/basic.js +101 -68
- package/dist/util/basic.js.map +1 -1
- package/dist/util/bytes.d.ts +13 -0
- package/dist/util/bytes.js +122 -0
- package/dist/util/bytes.js.map +1 -0
- package/package.json +1 -1
- package/src/build/BuildContext.ts +3 -1
- package/src/build/BuildMeta.ts +21 -7
- package/src/build/FileHandler.ts +152 -79
- package/src/check.ts +8 -4
- package/src/cmp/CopyFiles.ts +9 -2
- package/src/cmp/Fragment.ts +82 -6
- package/src/diff.ts +38 -19
- package/src/jostraca.ts +69 -37
- package/src/op/CopyOp.ts +17 -4
- package/src/op/FileOp.ts +18 -2
- package/src/op/FolderOp.ts +4 -3
- package/src/op/FragmentOp.ts +3 -0
- package/src/op/InjectOp.ts +21 -6
- package/src/op/ProjectOp.ts +25 -4
- package/src/op/SlotOp.ts +3 -0
- package/src/tree.ts +25 -2
- package/src/util/basic.ts +113 -71
- package/src/util/bytes.ts +158 -0
|
@@ -98,8 +98,10 @@ class BuildContext {
|
|
|
98
98
|
}
|
|
99
99
|
duplicateFolder() {
|
|
100
100
|
if (null == this.dfolder) {
|
|
101
|
+
// From the handler's canonical folder, not the raw option, so the
|
|
102
|
+
// baselines sit under the same folder the files do.
|
|
101
103
|
this.dfolder =
|
|
102
|
-
node_path_1.default.normalize(node_path_1.default.join(this.folder, this.bmeta.next.foldername, 'generated'));
|
|
104
|
+
node_path_1.default.normalize(node_path_1.default.join(this.fh.folder, this.bmeta.next.foldername, 'generated'));
|
|
103
105
|
}
|
|
104
106
|
return this.dfolder;
|
|
105
107
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BuildContext.js","sourceRoot":"","sources":["../../src/build/BuildContext.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"BuildContext.js","sourceRoot":"","sources":["../../src/build/BuildContext.ts"],"names":[],"mappings":";;;;;;AACA,0DAA4B;AAS5B,+CAEsB;AAEtB,2CAEoB;AAapB,MAAM,EAAE,GAAG,eAAe,CAAA;AAG1B,4DAA4D;AAC5D,MAAM,YAAY;IAEhB,EAAE,CAAW;IACb,GAAG,CAAc;IAEjB,KAAK,CAAW;IAChB,EAAE,CAAa;IACf,KAAK,CAAO;IACZ,IAAI,CAAQ;IACZ,GAAG,CAAK;IACR,MAAM,CAAQ;IACd,OAAO,CASN;IACD,GAAG,CAGF;IAED,kEAAkE;IAClE,SAAS,CAAqB;IAE9B,OAAO,CAAS;IAGhB,YACE,MAAc,EACd,QAAkB,EAClB,OAIC,EACD,EAAa,EACb,GAAiB;QAEjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;QACZ,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QAEd,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,EAAE,GAAG,iCAAiC,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,CAAA;QACrE,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,EAAE,CAAA;QAEjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,OAAO,GAAG;YACb,OAAO,EAAE;gBACP,IAAI,EAAE,SAAS,EAAE;aAClB;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS,EAAE;gBACjB,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,EAAE;aACT;YACD,4BAA4B;YAC5B,IAAI,EAAE,SAAS,EAAE;YACjB,OAAO,EAAE,SAAS;SACnB,CAAA;QACD,IAAI,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAA;QACpC,IAAI,CAAC,SAAS,GAAG,IAAI,GAAG,EAAE,CAAA;QAE1B,IAAI,CAAC,EAAE,GAAG,IAAI,yBAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;QAClD,IAAI,CAAC,KAAK,GAAG,IAAI,qBAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACrC,CAAC;IAGD,OAAO,CAAC,IAAY,EAAE,IAAS;QAC7B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IAC5B,CAAC;IAGD,sEAAsE;IACtE,qEAAqE;IACrE,mEAAmE;IACnE,0DAA0D;IAC1D,EAAE;IACF,kEAAkE;IAClE,oEAAoE;IACpE,gEAAgE;IAChE,mEAAmE;IACnE,0DAA0D;IAC1D,6DAA6D;IAC7D,oEAAoE;IACpE,qEAAqE;IACrE,kEAAkE;IAClE,gDAAgD;IAChD,EAAE;IACF,kEAAkE;IAClE,+DAA+D;IAC/D,mEAAmE;IACnE,oDAAoD;IACpD,EAAE;IACF,mEAAmE;IACnE,4BAA4B;IAC5B,SAAS,CAAC,QAAgB,EAAE,KAAa,EAAE,OAAe;QACxD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QAC5C,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,OAAO;gBAChC,6DAA6D;gBAC7D,QAAQ,GAAG,UAAU,GAAG,CAAC,OAAO,IAAI,QAAQ,CAAC;gBAC7C,WAAW,GAAG,CAAC,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAA;QACtC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IACrC,CAAC;IAGD,sEAAsE;IACtE,yEAAyE;IACzE,wEAAwE;IACxE,qEAAqE;IACrE,wEAAwE;IACxE,mDAAmD;IACnD,UAAU;QACR,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAA;QAC5C,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAClD,CAAC;IAGD,eAAe;QACb,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACzB,kEAAkE;YAClE,oDAAoD;YACpD,IAAI,CAAC,OAAO;gBACV,mBAAI,CAAC,SAAS,CACZ,mBAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAA;QACzE,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;CACF;QAUC,YAAY;AAPd,SAAS,SAAS;IAChB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;AAC1D,CAAC"}
|
package/dist/build/BuildMeta.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.BuildMeta = void 0;
|
|
7
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const FileHandler_1 = require("./FileHandler");
|
|
8
9
|
const basic_1 = require("../util/basic");
|
|
9
10
|
// Log non-fatal weirdness.
|
|
10
11
|
const dlog = (0, basic_1.getdlog)('jostraca', __filename);
|
|
@@ -46,7 +47,7 @@ class BuildMeta {
|
|
|
46
47
|
// save over previous
|
|
47
48
|
saveMetaData(this.fh, this.next);
|
|
48
49
|
if (false === this.fh.control.version) {
|
|
49
|
-
this.fh.saveFile(node_path_1.default.join(this.fh.folder, this.next.foldername, '.gitignore'), `
|
|
50
|
+
this.fh.saveFile((0, FileHandler_1.canonPath)(node_path_1.default.join(this.fh.folder, this.next.foldername, '.gitignore')), `
|
|
50
51
|
${this.next.filename}
|
|
51
52
|
generated
|
|
52
53
|
`);
|
|
@@ -58,13 +59,13 @@ exports.BuildMeta = BuildMeta;
|
|
|
58
59
|
function loadMetaData(fh, bmeta) {
|
|
59
60
|
// Full (folder-prefixed) path: the FileHandler FS methods use paths
|
|
60
61
|
// directly and no longer re-join `this.folder`.
|
|
61
|
-
const metapath = node_path_1.default.join(fh.folder, bmeta.foldername, bmeta.filename);
|
|
62
|
+
const metapath = (0, FileHandler_1.canonPath)(node_path_1.default.join(fh.folder, bmeta.foldername, bmeta.filename));
|
|
62
63
|
if (fh.existsFile(metapath)) {
|
|
63
64
|
try {
|
|
64
65
|
const json = fh.loadJSON(metapath);
|
|
65
|
-
bmeta.last =
|
|
66
|
-
bmeta.hlast =
|
|
67
|
-
bmeta.files = json?.files
|
|
66
|
+
bmeta.last = isTime(json?.last) ? json.last : -1;
|
|
67
|
+
bmeta.hlast = isTime(json?.hlast) ? json.hlast : -1;
|
|
68
|
+
bmeta.files = isPlainObject(json?.files) ? json.files : {};
|
|
68
69
|
}
|
|
69
70
|
catch (err) {
|
|
70
71
|
// A truncated or hand-edited meta log used to throw straight out of
|
|
@@ -82,10 +83,20 @@ function loadMetaData(fh, bmeta) {
|
|
|
82
83
|
}
|
|
83
84
|
return bmeta;
|
|
84
85
|
}
|
|
86
|
+
// A previous `last` is used only when a Date can carry it. A string, a
|
|
87
|
+
// boolean or 1e20 in a hand-edited log reached the merge labels and threw
|
|
88
|
+
// "Invalid time value"; anything else is treated as absent, as an
|
|
89
|
+
// unreadable log is.
|
|
90
|
+
function isTime(v) {
|
|
91
|
+
return 'number' === typeof v && Number.isFinite(v) && Math.abs(v) <= 8.64e15;
|
|
92
|
+
}
|
|
93
|
+
function isPlainObject(v) {
|
|
94
|
+
return null != v && 'object' === typeof v && !Array.isArray(v);
|
|
95
|
+
}
|
|
85
96
|
function saveMetaData(fh, bmeta) {
|
|
86
97
|
// Full (folder-prefixed) path: the FileHandler FS methods use paths
|
|
87
98
|
// directly and no longer re-join `this.folder`.
|
|
88
|
-
const metapath = node_path_1.default.join(fh.folder, bmeta.foldername, bmeta.filename);
|
|
99
|
+
const metapath = (0, FileHandler_1.canonPath)(node_path_1.default.join(fh.folder, bmeta.foldername, bmeta.filename));
|
|
89
100
|
fh.saveJSON(metapath, bmeta);
|
|
90
101
|
}
|
|
91
102
|
//# sourceMappingURL=BuildMeta.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BuildMeta.js","sourceRoot":"","sources":["../../src/build/BuildMeta.ts"],"names":[],"mappings":";;;;;;AACA,0DAA4B;
|
|
1
|
+
{"version":3,"file":"BuildMeta.js","sourceRoot":"","sources":["../../src/build/BuildMeta.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BuildContext } from './BuildContext';
|
|
2
2
|
import { FST, Audit } from '../types';
|
|
3
3
|
declare function canonPath(path: string): string;
|
|
4
|
+
declare function canonFolder(folder: string): string;
|
|
4
5
|
declare class FileHandler {
|
|
5
6
|
when: number;
|
|
6
7
|
fs: () => FST;
|
|
@@ -32,6 +33,7 @@ declare class FileHandler {
|
|
|
32
33
|
};
|
|
33
34
|
createdDirs: Set<string>;
|
|
34
35
|
savedPaths: Set<string>;
|
|
36
|
+
filelogged: Record<string, Set<string>>;
|
|
35
37
|
constructor(bctx: BuildContext, existing: {
|
|
36
38
|
txt: any;
|
|
37
39
|
bin: any;
|
|
@@ -44,10 +46,12 @@ declare class FileHandler {
|
|
|
44
46
|
withinFolder(path: string): boolean;
|
|
45
47
|
saveBinary(path: string, newContentSource: string | Buffer, whence?: string): void;
|
|
46
48
|
save(path: string, newContentSource: string | Buffer, write?: boolean | string, whence?: string, mode?: number, isText?: boolean): void;
|
|
49
|
+
private decision;
|
|
47
50
|
copy(frompath: string, topath: string, write?: boolean | string, whence?: string): void;
|
|
48
51
|
merge(generated: string, baseline: string, existing: string, why: string[]): {
|
|
49
52
|
content: string;
|
|
50
53
|
conflict: boolean;
|
|
54
|
+
outcome: string;
|
|
51
55
|
};
|
|
52
56
|
diff(generated: string, existing: string): string;
|
|
53
57
|
existsFile(path: string, whence?: string): boolean;
|
|
@@ -55,6 +59,8 @@ declare class FileHandler {
|
|
|
55
59
|
loadJSON(path: string, opts?: any | string, whence?: string): any;
|
|
56
60
|
saveJSON(path: string, json: any, opts?: any | string, whence?: string): any;
|
|
57
61
|
loadFile(path: string, opts?: any | string, whence?: string): string | Buffer;
|
|
62
|
+
loadSource(path: string, opts?: any | string, whence?: string): string | Buffer;
|
|
63
|
+
private load;
|
|
58
64
|
ensureFolder(path: string): void;
|
|
59
65
|
private ensureDir;
|
|
60
66
|
private chmodUnchanged;
|
|
@@ -65,4 +71,4 @@ declare class FileHandler {
|
|
|
65
71
|
declare function annotatedPath(target: string, kind: string): string;
|
|
66
72
|
declare function validName(name: any, kind: string, errmark: string): void;
|
|
67
73
|
declare function validPath(path: string, maxdepth: number, errmark: string): void;
|
|
68
|
-
export { annotatedPath, canonPath, validName, validPath, FileHandler };
|
|
74
|
+
export { annotatedPath, canonFolder, canonPath, validName, validPath, FileHandler };
|
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.FileHandler = void 0;
|
|
40
40
|
exports.annotatedPath = annotatedPath;
|
|
41
|
+
exports.canonFolder = canonFolder;
|
|
41
42
|
exports.canonPath = canonPath;
|
|
42
43
|
exports.validName = validName;
|
|
43
44
|
exports.validPath = validPath;
|
|
@@ -51,8 +52,9 @@ const CN = 'FileHandler:';
|
|
|
51
52
|
function fwd(p) {
|
|
52
53
|
return p.includes('\\') ? p.replace(/\\/g, '/') : p;
|
|
53
54
|
}
|
|
54
|
-
// THE ONE CANONICAL FORM OF AN OUTPUT PATH: separators
|
|
55
|
-
// and `..` segments resolved, forward slashes.
|
|
55
|
+
// THE ONE CANONICAL FORM OF AN OUTPUT PATH: separators folded, then `.`
|
|
56
|
+
// and `..` segments resolved, forward slashes. Folded FIRST, so that a
|
|
57
|
+
// backslash `..` segment resolves as a slash one does, as in Go.
|
|
56
58
|
//
|
|
57
59
|
// Exported because `save` is not the only place that has to agree about
|
|
58
60
|
// what "the same file" means. `FileOp.before` composes a path from the
|
|
@@ -63,7 +65,21 @@ function fwd(p) {
|
|
|
63
65
|
// case the guard exists to refuse. Go has never had it: `fileBefore`
|
|
64
66
|
// does `path.Clean(fwd(raw))` BEFORE recording anything.
|
|
65
67
|
function canonPath(path) {
|
|
66
|
-
return fwd(node_path_1.default.normalize(path));
|
|
68
|
+
return fwd(node_path_1.default.normalize(fwd(path)));
|
|
69
|
+
}
|
|
70
|
+
// The output folder, canonicalised once as canonPath does, with trailing
|
|
71
|
+
// separators stripped except from a filesystem root. A kept slash (`out/`)
|
|
72
|
+
// or an unresolved backslash `..` (`o\..\p`, when this normalised before
|
|
73
|
+
// folding) left the folder unlike every file path under it, so no path was
|
|
74
|
+
// inside it: meta keys kept the folder prefix, no baseline was written,
|
|
75
|
+
// and a later merge found no ancestor and overwrote the user's edits.
|
|
76
|
+
function canonFolder(folder) {
|
|
77
|
+
const norm = canonPath(folder);
|
|
78
|
+
if ('/' === norm || /^[A-Za-z]:\/$/.test(norm)) {
|
|
79
|
+
return norm;
|
|
80
|
+
}
|
|
81
|
+
const stripped = norm.replace(/\/+$/, '');
|
|
82
|
+
return '' === stripped ? '.' : stripped;
|
|
67
83
|
}
|
|
68
84
|
const JOSTRACA_PROTECT = 'JOSTRACA_PROTECT';
|
|
69
85
|
// Audit breadcrumb per merge outcome, so the `why` trail says which fast
|
|
@@ -126,11 +142,12 @@ class FileHandler {
|
|
|
126
142
|
files;
|
|
127
143
|
createdDirs;
|
|
128
144
|
savedPaths;
|
|
145
|
+
filelogged;
|
|
129
146
|
constructor(bctx, existing, control) {
|
|
130
147
|
this.fs = bctx.fs;
|
|
131
148
|
this.now = bctx.now;
|
|
132
149
|
this.when = bctx.when;
|
|
133
|
-
this.folder =
|
|
150
|
+
this.folder = canonFolder(bctx.folder);
|
|
134
151
|
this.audit = bctx.audit;
|
|
135
152
|
this.existing = existing;
|
|
136
153
|
this.control = control;
|
|
@@ -146,6 +163,7 @@ class FileHandler {
|
|
|
146
163
|
};
|
|
147
164
|
this.createdDirs = new Set();
|
|
148
165
|
this.savedPaths = new Set();
|
|
166
|
+
this.filelogged = {};
|
|
149
167
|
// Yikes!
|
|
150
168
|
this.duplicateFolder = bctx.duplicateFolder.bind(bctx);
|
|
151
169
|
this.last = () => bctx.bmeta.prev.last;
|
|
@@ -210,7 +228,7 @@ class FileHandler {
|
|
|
210
228
|
// baseline — `.jostraca/generated` joined to the relative path — nor-
|
|
211
229
|
// malize to a location outside the baseline directory entirely, and
|
|
212
230
|
// silently overwrite whatever was there.
|
|
213
|
-
const norm =
|
|
231
|
+
const norm = canonPath(path);
|
|
214
232
|
return '..' !== norm && !norm.startsWith('../');
|
|
215
233
|
}
|
|
216
234
|
if ('/' === this.folder) {
|
|
@@ -302,22 +320,17 @@ class FileHandler {
|
|
|
302
320
|
// Tracks whether the generated content actually differs from what is
|
|
303
321
|
// already on disk, so the write path can skip a no-op rewrite.
|
|
304
322
|
let unchanged = false;
|
|
323
|
+
// Whether the save ends in a write or skip record.
|
|
324
|
+
let final = false;
|
|
305
325
|
if (exists) {
|
|
306
326
|
why.push('exists-0');
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
// same bytes, and the unchanged-file optimisation was defeated for
|
|
315
|
-
// every binary. Issue #30. Go has always used bytes.Equal.
|
|
316
|
-
//
|
|
317
|
-
// Decoding cannot be fixed by converting one side afterwards: utf8
|
|
318
|
-
// decoding is lossy for binary, so the string no longer carries the
|
|
319
|
-
// bytes to compare.
|
|
320
|
-
let currentContent = this.loadFile(path, isTextFile ? undefined : { encoding: null });
|
|
327
|
+
// The existing file is handled as BYTES, text included, as Go does.
|
|
328
|
+
// Decoding it as UTF-8 turned every invalid byte into U+FFFD, so a
|
|
329
|
+
// file the user saved in Latin-1 was judged "unchanged" against a
|
|
330
|
+
// generate holding U+FFFD, and diff and merge wrote the replacement
|
|
331
|
+
// characters back over the user's bytes. Every comparison below is
|
|
332
|
+
// on bytes; diff and merge run over a byte-transparent latin1 form.
|
|
333
|
+
const currentContent = this.loadFile(path, { encoding: null });
|
|
321
334
|
const protect = 0 <= currentContent.indexOf(JOSTRACA_PROTECT);
|
|
322
335
|
meta.protect = protect;
|
|
323
336
|
unchanged = sameContent(currentContent, newContentSource);
|
|
@@ -327,7 +340,7 @@ class FileHandler {
|
|
|
327
340
|
why.push('protect-0');
|
|
328
341
|
write = false;
|
|
329
342
|
}
|
|
330
|
-
else if (!
|
|
343
|
+
else if (!unchanged) {
|
|
331
344
|
why.push('content-0');
|
|
332
345
|
let oldpath = annotatedPath(path, 'old');
|
|
333
346
|
this.copyFile(path, oldpath, whence + 'preserve:');
|
|
@@ -336,8 +349,7 @@ class FileHandler {
|
|
|
336
349
|
meta.action = 'preserve';
|
|
337
350
|
whenify(meta, this.now());
|
|
338
351
|
meta.actions.push(meta.action);
|
|
339
|
-
this.
|
|
340
|
-
{ ...meta, why, action: meta.action, path }]);
|
|
352
|
+
this.decision(CN + FN + wstr, meta, why, path);
|
|
341
353
|
}
|
|
342
354
|
}
|
|
343
355
|
if (existing.write && !protect) {
|
|
@@ -346,7 +358,7 @@ class FileHandler {
|
|
|
346
358
|
}
|
|
347
359
|
else if (existing.present) {
|
|
348
360
|
why.push('present-0');
|
|
349
|
-
if (!
|
|
361
|
+
if (!unchanged) {
|
|
350
362
|
why.push('content-1');
|
|
351
363
|
let newpath = annotatedPath(path, 'new');
|
|
352
364
|
this.saveFile(newpath, newContentSource, { flush: true }, whence + 'present:');
|
|
@@ -354,8 +366,7 @@ class FileHandler {
|
|
|
354
366
|
meta.action = 'present';
|
|
355
367
|
whenify(meta, this.now());
|
|
356
368
|
meta.actions.push(meta.action);
|
|
357
|
-
this.
|
|
358
|
-
{ ...meta, why, action: meta.action, path }]);
|
|
369
|
+
this.decision(CN + FN + wstr, meta, why, path);
|
|
359
370
|
}
|
|
360
371
|
}
|
|
361
372
|
if (!protect) {
|
|
@@ -363,13 +374,12 @@ class FileHandler {
|
|
|
363
374
|
if (existing.diff) {
|
|
364
375
|
why.push('diff-0');
|
|
365
376
|
write = false;
|
|
366
|
-
if (!
|
|
377
|
+
if (!unchanged) {
|
|
367
378
|
why.push('content-2');
|
|
368
379
|
meta.action = 'diff';
|
|
369
|
-
const newContent =
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
this.saveFile(path, diffContent, { encoding: 'utf8', ...modeopts() }, whence + meta.action);
|
|
380
|
+
const newContent = latin1(newContentSource);
|
|
381
|
+
const diffContent = this.diff(newContent, latin1(currentContent));
|
|
382
|
+
this.saveFile(path, Buffer.from(diffContent, 'latin1'), modeopts(), whence + meta.action);
|
|
373
383
|
// this.files.diffed.push(path)
|
|
374
384
|
this.filelog('diffed', path);
|
|
375
385
|
const conflict = newContent !== diffContent;
|
|
@@ -380,8 +390,7 @@ class FileHandler {
|
|
|
380
390
|
whenify(meta, this.now());
|
|
381
391
|
meta.actions.push(meta.action);
|
|
382
392
|
meta.conflict = conflict;
|
|
383
|
-
this.
|
|
384
|
-
{ ...meta, why, action: meta.action, path }]);
|
|
393
|
+
this.decision(CN + FN + wstr, meta, why, path);
|
|
385
394
|
}
|
|
386
395
|
else {
|
|
387
396
|
// Equal content is still not a no-op when an explicit mode was
|
|
@@ -396,7 +405,7 @@ class FileHandler {
|
|
|
396
405
|
}
|
|
397
406
|
else if (existing.merge) {
|
|
398
407
|
why.push('merge-0');
|
|
399
|
-
if (!
|
|
408
|
+
if (!unchanged) {
|
|
400
409
|
why.push('content-3');
|
|
401
410
|
if (this.control.duplicate) {
|
|
402
411
|
why.push('duplicate-0');
|
|
@@ -406,16 +415,27 @@ class FileHandler {
|
|
|
406
415
|
why.push('dupexists-0');
|
|
407
416
|
write = false;
|
|
408
417
|
meta.action = 'merge';
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
prevGenContent, // baseline (last generate)
|
|
414
|
-
currentContent.toString(), // existing (on disk)
|
|
418
|
+
const prevGenContent = this.loadFile(dpath, { encoding: null });
|
|
419
|
+
const mergeres = this.merge(latin1(newContentSource), // generated
|
|
420
|
+
latin1(prevGenContent), // baseline (last generate)
|
|
421
|
+
latin1(currentContent), // existing (on disk)
|
|
415
422
|
why);
|
|
416
423
|
const diffcontent = mergeres.content;
|
|
417
|
-
|
|
418
|
-
|
|
424
|
+
// A file still holding an earlier merge's markers is left
|
|
425
|
+
// byte-for-byte untouched, and reported conflicted: it
|
|
426
|
+
// still carries markers, and the documented guard on
|
|
427
|
+
// files.conflicted must not pass over it. Rewriting it
|
|
428
|
+
// bumped the mtime for nothing.
|
|
429
|
+
const unresolved = 'unresolved' === mergeres.outcome;
|
|
430
|
+
const conflict = mergeres.conflict || unresolved;
|
|
431
|
+
if (unresolved) {
|
|
432
|
+
if (this.chmodUnchanged(path, mode)) {
|
|
433
|
+
why.push('chmod-0');
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
this.saveFile(path, Buffer.from(diffcontent, 'latin1'), modeopts(), whence + meta.action);
|
|
438
|
+
}
|
|
419
439
|
// this.files.merged.push(path)
|
|
420
440
|
this.filelog('merged', path);
|
|
421
441
|
if (conflict) {
|
|
@@ -425,8 +445,7 @@ class FileHandler {
|
|
|
425
445
|
whenify(meta, this.now());
|
|
426
446
|
meta.actions.push(meta.action);
|
|
427
447
|
meta.conflict = conflict;
|
|
428
|
-
this.
|
|
429
|
-
{ ...meta, why, action: meta.action, path }]);
|
|
448
|
+
this.decision(CN + FN + wstr, meta, why, path);
|
|
430
449
|
}
|
|
431
450
|
}
|
|
432
451
|
}
|
|
@@ -473,15 +492,14 @@ class FileHandler {
|
|
|
473
492
|
}
|
|
474
493
|
meta.actions.push(meta.action);
|
|
475
494
|
whenify(meta, this.now());
|
|
476
|
-
|
|
477
|
-
{ ...meta, why, action: meta.action, path }]);
|
|
495
|
+
final = true;
|
|
478
496
|
}
|
|
479
497
|
else if (0 === meta.actions.length) {
|
|
480
498
|
why.push('skip-0');
|
|
481
499
|
meta.action = 'skip';
|
|
482
500
|
meta.actions.push(meta.action);
|
|
483
|
-
this.
|
|
484
|
-
|
|
501
|
+
whenify(meta, this.now());
|
|
502
|
+
final = true;
|
|
485
503
|
}
|
|
486
504
|
if (this.control.duplicate) {
|
|
487
505
|
why.push('duplicate-1');
|
|
@@ -493,14 +511,23 @@ class FileHandler {
|
|
|
493
511
|
this.ensureDir(fwd(node_path_1.default.dirname(dpath)));
|
|
494
512
|
this.writeFileAtomic(dpath, newContentSource, { flush: true });
|
|
495
513
|
}
|
|
496
|
-
if (null == meta.when) {
|
|
497
|
-
whenify(meta, this.now());
|
|
498
|
-
}
|
|
499
514
|
}
|
|
500
515
|
}
|
|
501
|
-
//
|
|
516
|
+
// The write or skip record is the save's last word, so it carries the
|
|
517
|
+
// baseline breadcrumbs too.
|
|
518
|
+
if (final) {
|
|
519
|
+
this.decision(CN + FN + wstr, meta, why, path);
|
|
520
|
+
}
|
|
502
521
|
this.addmeta(path, meta);
|
|
503
522
|
}
|
|
523
|
+
// Push a decision record as a SNAPSHOT. The record used to share the live
|
|
524
|
+
// `why` and `meta.actions` arrays, so an early record (a preserve) later
|
|
525
|
+
// reported the whole save's actions and breadcrumbs.
|
|
526
|
+
decision(tag, meta, why, path) {
|
|
527
|
+
this.audit.push([tag + meta.action, {
|
|
528
|
+
...meta, actions: [...meta.actions], why: [...why], action: meta.action, path,
|
|
529
|
+
}]);
|
|
530
|
+
}
|
|
504
531
|
copy(frompath, topath, write, whence) {
|
|
505
532
|
const wstr = null == whence ? '' : whence + ':';
|
|
506
533
|
const FN = 'copy:';
|
|
@@ -533,7 +560,7 @@ class FileHandler {
|
|
|
533
560
|
//
|
|
534
561
|
// CopyOp's own copyFile reads a Buffer and go/build.go reads bytes;
|
|
535
562
|
// this is now the same shape as both, with no order to get wrong.
|
|
536
|
-
const raw = this.
|
|
563
|
+
const raw = this.loadSource(frompath, { encoding: null }, whence);
|
|
537
564
|
// The SOURCE decides: a binary source stays governed by `existing.bin`
|
|
538
565
|
// even when copied to a destination whose extension is not on the
|
|
539
566
|
// list. Same rule as CopyOp's own copy paths and go/build.go's
|
|
@@ -562,7 +589,7 @@ class FileHandler {
|
|
|
562
589
|
kind: 'merge',
|
|
563
590
|
});
|
|
564
591
|
why.push(MERGE_WHY[res.outcome]);
|
|
565
|
-
return { content: res.content, conflict: res.conflict };
|
|
592
|
+
return { content: res.content, conflict: res.conflict, outcome: res.outcome };
|
|
566
593
|
}
|
|
567
594
|
// Annotated two-way view of the difference between the new generate and
|
|
568
595
|
// what is on disk.
|
|
@@ -582,7 +609,7 @@ class FileHandler {
|
|
|
582
609
|
// Paths are canonical (already folder-prefixed by the build phase, or
|
|
583
610
|
// absolute); use them directly. Do NOT re-join `this.folder`, which would
|
|
584
611
|
// double-prefix relative non-`.` output folders. Matches the Go port.
|
|
585
|
-
const fullpath =
|
|
612
|
+
const fullpath = canonPath(path);
|
|
586
613
|
try {
|
|
587
614
|
const exists = fs.existsSync(fullpath);
|
|
588
615
|
this.audit.push([CN + FN + wstr,
|
|
@@ -605,8 +632,8 @@ class FileHandler {
|
|
|
605
632
|
validPath(frompath, this.maxdepth, CN + FN + 'from:' + wstr);
|
|
606
633
|
validPath(topath, this.maxdepth, CN + FN + 'to:' + wstr);
|
|
607
634
|
// Canonical paths: use directly, do not re-join `this.folder` (see existsFile).
|
|
608
|
-
const fulltopath =
|
|
609
|
-
const fullfrompath =
|
|
635
|
+
const fulltopath = canonPath(topath);
|
|
636
|
+
const fullfrompath = canonPath(frompath);
|
|
610
637
|
try {
|
|
611
638
|
const existed = fs.existsSync(fulltopath);
|
|
612
639
|
// Copy BYTES, always — never decode as UTF-8 first.
|
|
@@ -657,7 +684,7 @@ class FileHandler {
|
|
|
657
684
|
const cstr = 'string' === typeof content ? content : content.toString(opts.encoding);
|
|
658
685
|
const json = JSON.parse(cstr);
|
|
659
686
|
this.audit.push([CN + FN + wstr,
|
|
660
|
-
{ path, when, size: content
|
|
687
|
+
{ path, when, size: byteLength(content) }]);
|
|
661
688
|
return json;
|
|
662
689
|
}
|
|
663
690
|
catch (err) {
|
|
@@ -683,7 +710,7 @@ class FileHandler {
|
|
|
683
710
|
const jstr = 'string' === typeof json ? json : JSON.stringify(json, null, 2);
|
|
684
711
|
this.saveFile(path, jstr, opts, whence);
|
|
685
712
|
this.audit.push([CN + FN + wstr,
|
|
686
|
-
{ path, when, size: jstr
|
|
713
|
+
{ path, when, size: byteLength(jstr) }]);
|
|
687
714
|
return jstr;
|
|
688
715
|
}
|
|
689
716
|
catch (err) {
|
|
@@ -694,6 +721,18 @@ class FileHandler {
|
|
|
694
721
|
}
|
|
695
722
|
}
|
|
696
723
|
loadFile(path, opts, whence) {
|
|
724
|
+
return this.load(path, canonPath(path), opts, whence);
|
|
725
|
+
}
|
|
726
|
+
// loadFile for the SOURCE of a copy, read at the path as given. A source
|
|
727
|
+
// keeps its platform meaning, as every other source read does (CopyOp
|
|
728
|
+
// stats and reads its entries raw): on POSIX a backslash is an ordinary
|
|
729
|
+
// name character. Folding it here, as an output path is folded, sent a
|
|
730
|
+
// binary tree entry named `b\in.png` to `b/in.png`, and the copy failed
|
|
731
|
+
// with ENOENT while the text entry beside it was copied.
|
|
732
|
+
loadSource(path, opts, whence) {
|
|
733
|
+
return this.load(path, path, opts, whence);
|
|
734
|
+
}
|
|
735
|
+
load(path, fullpath, opts, whence) {
|
|
697
736
|
const when = this.now();
|
|
698
737
|
const wstr = null == whence ? '' : whence + ':';
|
|
699
738
|
const fs = this.fs();
|
|
@@ -708,11 +747,10 @@ class FileHandler {
|
|
|
708
747
|
opts.encoding = undefined === opts.encoding ? 'utf8' : opts.encoding;
|
|
709
748
|
validPath(path, this.maxdepth, CN + FN + wstr);
|
|
710
749
|
try {
|
|
711
|
-
//
|
|
712
|
-
const fullpath = fwd(node_path_1.default.normalize(path));
|
|
750
|
+
// Used directly, never re-joined to `this.folder` (see existsFile).
|
|
713
751
|
const content = fs.readFileSync(fullpath, opts);
|
|
714
752
|
this.audit.push([CN + FN + wstr,
|
|
715
|
-
{ path, when, size: content
|
|
753
|
+
{ path, when, size: byteLength(content) }]);
|
|
716
754
|
return content;
|
|
717
755
|
}
|
|
718
756
|
catch (err) {
|
|
@@ -816,9 +854,10 @@ class FileHandler {
|
|
|
816
854
|
// is the R12 case, and `created` must stay false so the cleanup
|
|
817
855
|
// does not delete somebody else's file.
|
|
818
856
|
//
|
|
819
|
-
// Any OTHER error
|
|
820
|
-
//
|
|
821
|
-
// remove. Without this it survived the failed build.
|
|
857
|
+
// Any OTHER error may have come AFTER the create succeeded
|
|
858
|
+
// (ENOSPC, EIO), leaving a partial temp file that is ours to
|
|
859
|
+
// remove. Without this it survived the failed build. If the
|
|
860
|
+
// create itself failed (EACCES), the cleanup finds nothing.
|
|
822
861
|
if ('EEXIST' !== err?.code) {
|
|
823
862
|
created = true;
|
|
824
863
|
}
|
|
@@ -859,7 +898,10 @@ class FileHandler {
|
|
|
859
898
|
}
|
|
860
899
|
}
|
|
861
900
|
catch (cleanuperr) {
|
|
862
|
-
|
|
901
|
+
// Already gone is not a failed cleanup: nothing was left behind.
|
|
902
|
+
if ('ENOENT' !== cleanuperr?.code) {
|
|
903
|
+
dlog('writeFileAtomic', 'temp cleanup failed: ' + tmppath);
|
|
904
|
+
}
|
|
863
905
|
}
|
|
864
906
|
throw err;
|
|
865
907
|
}
|
|
@@ -884,7 +926,7 @@ class FileHandler {
|
|
|
884
926
|
}
|
|
885
927
|
try {
|
|
886
928
|
// Canonical path: use directly, do not re-join `this.folder` (see existsFile).
|
|
887
|
-
path =
|
|
929
|
+
path = canonPath(path);
|
|
888
930
|
const fullpath = path;
|
|
889
931
|
const parentfolder = fwd(node_path_1.default.dirname(fullpath));
|
|
890
932
|
const existed = fs.existsSync(fullpath);
|
|
@@ -893,25 +935,29 @@ class FileHandler {
|
|
|
893
935
|
this.writeFileAtomic(fullpath, content, opts, opts.mode);
|
|
894
936
|
}
|
|
895
937
|
this.audit.push([CN + FN + wstr,
|
|
896
|
-
{ path, when, existed, size: content
|
|
938
|
+
{ path, when, existed, size: byteLength(content) }]);
|
|
897
939
|
}
|
|
898
940
|
catch (err) {
|
|
899
941
|
this.audit.push(['ERROR:' + CN + FN + wstr,
|
|
900
|
-
{ path, when, size: content
|
|
942
|
+
{ path, when, size: byteLength(content), err }]);
|
|
901
943
|
err.message = CN + FN + wstr + ' path=' + path + ':' + err.message;
|
|
902
944
|
throw err;
|
|
903
945
|
}
|
|
904
946
|
}
|
|
947
|
+
// A path appears at most once per kind, at its first position. Only the
|
|
948
|
+
// LAST entry used to be checked, so File t, File u, Inject t listed t
|
|
949
|
+
// twice and the list depended on sibling order.
|
|
905
950
|
filelog(kind, path) {
|
|
906
951
|
path = fwd(path);
|
|
907
952
|
let files = this.files;
|
|
908
953
|
if (files[kind]) {
|
|
909
|
-
const
|
|
910
|
-
if (path
|
|
954
|
+
const seen = this.filelogged[kind] = this.filelogged[kind] || new Set();
|
|
955
|
+
if (seen.has(path)) {
|
|
911
956
|
dlog('filelog', kind, 'duplicate: ' + path);
|
|
912
957
|
}
|
|
913
958
|
else {
|
|
914
|
-
|
|
959
|
+
seen.add(path);
|
|
960
|
+
files[kind].push(path);
|
|
915
961
|
}
|
|
916
962
|
}
|
|
917
963
|
else {
|
|
@@ -920,6 +966,19 @@ class FileHandler {
|
|
|
920
966
|
}
|
|
921
967
|
}
|
|
922
968
|
exports.FileHandler = FileHandler;
|
|
969
|
+
// The byte-transparent string form of content: one char per byte, so the
|
|
970
|
+
// diff engine's line splitting and equality are byte-exact and a round trip
|
|
971
|
+
// through Buffer.from(s, 'latin1') restores every byte. For valid UTF-8 the
|
|
972
|
+
// engine's output is byte-identical to running it on the decoded text.
|
|
973
|
+
function latin1(content) {
|
|
974
|
+
return (Buffer.isBuffer(content) ? content : Buffer.from(content, 'utf8'))
|
|
975
|
+
.toString('latin1');
|
|
976
|
+
}
|
|
977
|
+
// Audit sizes are byte lengths, whether the content is a string or a
|
|
978
|
+
// Buffer; `length` counted UTF-16 units for a string.
|
|
979
|
+
function byteLength(content) {
|
|
980
|
+
return 'string' === typeof content ? Buffer.byteLength(content, 'utf8') : content.length;
|
|
981
|
+
}
|
|
923
982
|
function whenify(meta, now) {
|
|
924
983
|
meta.when = now;
|
|
925
984
|
meta.hwhen = (0, basic_1.humanify)(now);
|