jostraca 0.15.1 → 0.16.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.
Files changed (46) hide show
  1. package/README.md +0 -11
  2. package/dist/build/BuildContext.d.ts +37 -0
  3. package/dist/build/BuildContext.js +54 -0
  4. package/dist/build/BuildContext.js.map +1 -0
  5. package/dist/build/BuildMeta.d.ts +24 -0
  6. package/dist/build/BuildMeta.js +60 -0
  7. package/dist/build/BuildMeta.js.map +1 -0
  8. package/dist/build/FileHandler.d.ts +47 -0
  9. package/dist/build/FileHandler.js +424 -0
  10. package/dist/build/FileHandler.js.map +1 -0
  11. package/dist/cmp/Copy.js +16 -3
  12. package/dist/cmp/Copy.js.map +1 -1
  13. package/dist/cmp/None.d.ts +2 -0
  14. package/dist/cmp/None.js +9 -0
  15. package/dist/cmp/None.js.map +1 -0
  16. package/dist/cmp/Project.js +1 -1
  17. package/dist/cmp/Project.js.map +1 -1
  18. package/dist/jostraca.d.ts +54 -28
  19. package/dist/jostraca.js +71 -173
  20. package/dist/jostraca.js.map +1 -1
  21. package/dist/op/CopyOp.d.ts +1 -1
  22. package/dist/op/CopyOp.js +13 -9
  23. package/dist/op/CopyOp.js.map +1 -1
  24. package/dist/op/FileOp.js +3 -1
  25. package/dist/op/FileOp.js.map +1 -1
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/dist/types.d.ts +18 -33
  28. package/dist/types.js +1 -1
  29. package/dist/types.js.map +1 -1
  30. package/dist/{utility.d.ts → util/basic.d.ts} +5 -1
  31. package/dist/{utility.js → util/basic.js} +40 -9
  32. package/dist/util/basic.js.map +1 -0
  33. package/package.json +8 -7
  34. package/src/build/BuildContext.ts +129 -0
  35. package/src/build/BuildMeta.ts +104 -0
  36. package/src/build/FileHandler.ts +581 -0
  37. package/src/cmp/Copy.ts +18 -4
  38. package/src/cmp/None.ts +13 -0
  39. package/src/cmp/Project.ts +1 -1
  40. package/src/jostraca.ts +85 -184
  41. package/src/op/CopyOp.ts +22 -12
  42. package/src/op/FileOp.ts +4 -1
  43. package/src/types.ts +30 -32
  44. package/src/util/basic.ts +614 -0
  45. package/dist/utility.js.map +0 -1
  46. /package/src/{utility.ts → util/basic.ts~} +0 -0
package/README.md CHANGED
@@ -4,16 +4,5 @@
4
4
  A code and project generator that uses React-style components.
5
5
 
6
6
 
7
- ## Options
8
-
9
- <!--START-OPTIONS-->
10
-
11
- * __folder__: `string`
12
- * __meta__: `any`
13
- * __exclude__: `boolean`
14
- * __fs__: `any`
15
- <!--END-OPTIONS-->
16
-
17
-
18
7
 
19
8
 
@@ -0,0 +1,37 @@
1
+ import { Node, FST, Audit } from '../types';
2
+ import { FileHandler } from './FileHandler';
3
+ import { BuildMeta } from './BuildMeta';
4
+ import type { Existing } from '../jostraca';
5
+ declare class BuildContext {
6
+ fs: () => FST;
7
+ now: () => number;
8
+ bmeta: BuildMeta;
9
+ fh: FileHandler;
10
+ audit: Audit;
11
+ when: number;
12
+ vol: any;
13
+ folder: string;
14
+ current: {
15
+ project: {
16
+ node: Node;
17
+ };
18
+ folder: {
19
+ node: Node;
20
+ parent: string;
21
+ path: string[];
22
+ };
23
+ file: Node;
24
+ content: any;
25
+ };
26
+ log: {
27
+ exclude: string[];
28
+ last: number;
29
+ };
30
+ dfolder?: string;
31
+ constructor(folder: string, existing: Existing, processing: {
32
+ duplicate: boolean;
33
+ }, fs: () => FST, now: () => number);
34
+ addmeta(file: string, meta: any): void;
35
+ duplicateFolder(): string;
36
+ }
37
+ export { BuildContext };
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BuildContext = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const FileHandler_1 = require("./FileHandler");
9
+ const BuildMeta_1 = require("./BuildMeta");
10
+ const CN = 'BuildContext:';
11
+ // TODO: rename meta folder to build, move into build folder
12
+ class BuildContext {
13
+ constructor(folder, existing, processing, fs, now) {
14
+ this.fs = fs;
15
+ this.now = now;
16
+ if (!this.fs().existsSync) {
17
+ throw new Error(CN + ' Invalid file system provider: ' + this.fs());
18
+ }
19
+ this.audit = [];
20
+ this.when = now();
21
+ this.folder = folder;
22
+ this.current = {
23
+ project: {
24
+ node: emptyNode(),
25
+ },
26
+ folder: {
27
+ node: emptyNode(),
28
+ parent: folder,
29
+ path: [],
30
+ },
31
+ // TODO: should be file.node
32
+ file: emptyNode(),
33
+ content: undefined,
34
+ };
35
+ this.log = { exclude: [], last: -1 };
36
+ this.fh = new FileHandler_1.FileHandler(this, existing, processing.duplicate);
37
+ this.bmeta = new BuildMeta_1.BuildMeta(this.fh);
38
+ }
39
+ addmeta(file, meta) {
40
+ this.bmeta.add(file, meta);
41
+ }
42
+ duplicateFolder() {
43
+ if (null == this.dfolder) {
44
+ this.dfolder =
45
+ node_path_1.default.normalize(node_path_1.default.join(this.folder, this.bmeta.next.foldername, 'generated'));
46
+ }
47
+ return this.dfolder;
48
+ }
49
+ }
50
+ exports.BuildContext = BuildContext;
51
+ function emptyNode() {
52
+ return { kind: 'none', path: [], meta: {}, content: [] };
53
+ }
54
+ //# sourceMappingURL=BuildContext.js.map
@@ -0,0 +1 @@
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;IA6BhB,YACE,MAAc,EACd,QAAkB,EAClB,UAEC,EACD,EAAa,EACb,GAAiB;QAGjB,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;QAEpC,IAAI,CAAC,EAAE,GAAG,IAAI,yBAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;QAC/D,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,eAAe;QACb,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO;gBACV,mBAAI,CAAC,SAAS,CACZ,mBAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAA;QACtE,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;CACF;AAUC,oCAAY;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"}
@@ -0,0 +1,24 @@
1
+ import { FileHandler } from './FileHandler';
2
+ type FileMetaData = {
3
+ path: string;
4
+ size: number;
5
+ last: number;
6
+ exclude: boolean;
7
+ };
8
+ type BuildMetaData = {
9
+ foldername: string;
10
+ filename: string;
11
+ last: number;
12
+ hlast: number;
13
+ files: Record<string, FileMetaData>;
14
+ };
15
+ declare class BuildMeta {
16
+ fh: FileHandler;
17
+ prev: BuildMetaData;
18
+ next: BuildMetaData;
19
+ constructor(fh: FileHandler);
20
+ last(): number;
21
+ add(file: string, meta: any): void;
22
+ done(): BuildMetaData;
23
+ }
24
+ export { BuildMeta };
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BuildMeta = void 0;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const basic_1 = require("../util/basic");
9
+ // Handle loading, recording,and saving of build meta data
10
+ class BuildMeta {
11
+ constructor(fh) {
12
+ this.fh = fh;
13
+ // TODO: file folder and name default can be overriden by jopts
14
+ this.prev = {
15
+ foldername: '.jostraca',
16
+ filename: 'jostraca.meta.log',
17
+ last: -1,
18
+ hlast: -1,
19
+ files: {}
20
+ };
21
+ this.prev = loadMetaData(this.fh, this.prev);
22
+ // TODO: load prev if exists
23
+ this.next = {
24
+ foldername: this.prev.foldername,
25
+ filename: this.prev.filename,
26
+ last: -1,
27
+ hlast: -1,
28
+ files: {}
29
+ };
30
+ }
31
+ last() {
32
+ return this.prev.last;
33
+ }
34
+ add(file, meta) {
35
+ this.next.files[file] = meta;
36
+ }
37
+ done() {
38
+ this.next.last = this.fh.now();
39
+ this.next.hlast = (0, basic_1.humanify)(this.next.last);
40
+ // save over previous
41
+ saveMetaData(this.fh, this.next);
42
+ return this.next;
43
+ }
44
+ }
45
+ exports.BuildMeta = BuildMeta;
46
+ function loadMetaData(fh, bmeta) {
47
+ const metapath = node_path_1.default.join(bmeta.foldername, bmeta.filename);
48
+ if (fh.existsFile(metapath)) {
49
+ const json = fh.loadJSON(metapath);
50
+ bmeta.last = json.last;
51
+ bmeta.hlast = json.hlast;
52
+ bmeta.files = json.files;
53
+ }
54
+ return bmeta;
55
+ }
56
+ function saveMetaData(fh, bmeta) {
57
+ const metapath = node_path_1.default.join(bmeta.foldername, bmeta.filename);
58
+ fh.saveJSON(metapath, bmeta);
59
+ }
60
+ //# sourceMappingURL=BuildMeta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BuildMeta.js","sourceRoot":"","sources":["../../src/build/BuildMeta.ts"],"names":[],"mappings":";;;;;;AACA,0DAA4B;AAI5B,yCAAwC;AAmBxC,0DAA0D;AAC1D,MAAM,SAAS;IAOb,YACE,EAAe;QAEf,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;QAEZ,+DAA+D;QAC/D,IAAI,CAAC,IAAI,GAAG;YACV,UAAU,EAAE,WAAW;YACvB,QAAQ,EAAE,mBAAmB;YAC7B,IAAI,EAAE,CAAC,CAAC;YACR,KAAK,EAAE,CAAC,CAAC;YACT,KAAK,EAAE,EAAE;SACV,CAAA;QAED,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAE5C,4BAA4B;QAE5B,IAAI,CAAC,IAAI,GAAG;YACV,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU;YAChC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ;YAC5B,IAAI,EAAE,CAAC,CAAC;YACR,KAAK,EAAE,CAAC,CAAC;YACT,KAAK,EAAE,EAAE;SACV,CAAA;IAEH,CAAC;IAGD,IAAI;QACF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;IACvB,CAAC;IAGD,GAAG,CAAC,IAAY,EAAE,IAAS;QACzB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAC9B,CAAC;IAGD,IAAI;QACF,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAA;QAC9B,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAA,gBAAQ,EAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE1C,qBAAqB;QACrB,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;QAEhC,OAAO,IAAI,CAAC,IAAI,CAAA;IAClB,CAAC;CACF;AAsBC,8BAAS;AAnBX,SAAS,YAAY,CAAC,EAAe,EAAE,KAAoB;IACzD,MAAM,QAAQ,GAAG,mBAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC5D,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;QAClC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;QACtB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACxB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IAC1B,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAGD,SAAS,YAAY,CAAC,EAAe,EAAE,KAAoB;IACzD,MAAM,QAAQ,GAAG,mBAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC5D,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;AAC9B,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { BuildContext } from './BuildContext';
2
+ import { FST, Audit } from '../types';
3
+ declare class FileHandler {
4
+ when: number;
5
+ fs: () => FST;
6
+ now: () => number;
7
+ folder: string;
8
+ audit: Audit;
9
+ maxdepth: number;
10
+ existing: {
11
+ txt: any;
12
+ bin: any;
13
+ };
14
+ duplicate: boolean;
15
+ duplicateFolder: () => string;
16
+ last: () => number;
17
+ addmeta: (file: string, meta: any) => void;
18
+ metafile: () => string;
19
+ files: {
20
+ preserved: string[];
21
+ written: string[];
22
+ presented: string[];
23
+ diffed: string[];
24
+ merged: string[];
25
+ conflicted: string[];
26
+ unchanged: string[];
27
+ };
28
+ constructor(bctx: BuildContext, existing: {
29
+ txt: any;
30
+ bin: any;
31
+ }, duplicate: boolean);
32
+ save(path: string, content: string | Buffer, write?: boolean | string, whence?: string): void;
33
+ copy(frompath: string, topath: string, write?: boolean | string, whence?: string): void;
34
+ merge(newcontent: string, oldcontent: string, origcontent: string): {
35
+ content: string;
36
+ conflict: boolean;
37
+ };
38
+ diff(oldcontent: string, newcontent: string): string;
39
+ existsFile(path: string, whence?: string): boolean;
40
+ copyFile(frompath: string, topath: string, whence?: string): void;
41
+ loadJSON(path: string, opts?: any | string, whence?: string): any;
42
+ saveJSON(path: string, json: any, opts?: any | string, whence?: string): any;
43
+ loadFile(path: string, opts?: any | string, whence?: string): string | Buffer;
44
+ saveFile(path: string, content: string | Buffer, opts?: any | string, whence?: string, original?: string | Buffer): void;
45
+ }
46
+ declare function validPath(path: string, maxdepth: number, errmark: string): void;
47
+ export { validPath, FileHandler };
@@ -0,0 +1,424 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FileHandler = void 0;
7
+ exports.validPath = validPath;
8
+ const Diff = require('diff');
9
+ const Diff3 = require('node-diff3');
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const basic_1 = require("../util/basic");
12
+ const CN = 'FileHandler:';
13
+ const JOSTRACA_PROTECT = 'JOSTRACA_PROTECT';
14
+ // TODO: if EOL != '\n', normalize to '\n' in load,save
15
+ class FileHandler {
16
+ constructor(bctx, existing, duplicate) {
17
+ this.fs = bctx.fs;
18
+ this.now = bctx.now;
19
+ this.when = bctx.when;
20
+ this.folder = node_path_1.default.normalize(bctx.folder);
21
+ this.audit = bctx.audit;
22
+ this.existing = existing;
23
+ this.duplicate = duplicate;
24
+ this.maxdepth = 22; // TODO: get from JostracaOptions
25
+ this.files = {
26
+ preserved: [],
27
+ written: [],
28
+ presented: [],
29
+ diffed: [],
30
+ merged: [],
31
+ conflicted: [],
32
+ unchanged: [],
33
+ };
34
+ // Yikes!
35
+ this.duplicateFolder = bctx.duplicateFolder.bind(bctx);
36
+ this.last = () => bctx.bmeta.prev.last;
37
+ this.addmeta = bctx.addmeta.bind(bctx);
38
+ this.metafile = () => bctx.bmeta.next.filename;
39
+ if (!this.fs().existsSync) {
40
+ throw new Error(CN + ' Invalid file system provider: ' + this.fs());
41
+ }
42
+ }
43
+ save(path, content, write, whence) {
44
+ const wstr = null == whence ? '' : whence + ':';
45
+ const fs = this.fs();
46
+ const FN = 'save:';
47
+ if ('string' === typeof write) {
48
+ whence = write;
49
+ write = false;
50
+ }
51
+ else if (null == write) {
52
+ write = false;
53
+ }
54
+ whence = null == whence ? '' : whence;
55
+ const existing = 'string' === typeof content ? this.existing.txt : this.existing.bin;
56
+ path = node_path_1.default.normalize(path);
57
+ const folder = node_path_1.default.dirname(path);
58
+ const exists = fs.existsSync(path);
59
+ write = write || !exists;
60
+ const meta = {
61
+ action: 'init',
62
+ path,
63
+ exists,
64
+ actions: [],
65
+ protect: false,
66
+ conflict: false,
67
+ };
68
+ if (exists) {
69
+ // let oldcontent = fs.readFileSync(path, 'utf8').toString()
70
+ let oldcontent = this.loadFile(path);
71
+ const protect = 0 <= oldcontent.indexOf(JOSTRACA_PROTECT);
72
+ meta.protect = protect;
73
+ if (existing.preserve) {
74
+ if (protect) {
75
+ write = false;
76
+ }
77
+ else if (oldcontent.length !== content.length || oldcontent !== content) {
78
+ let oldpath = node_path_1.default.join(folder, node_path_1.default.basename(path).replace(/\.[^.]+$/, '') +
79
+ '.old' + node_path_1.default.extname(path));
80
+ this.copyFile(path, oldpath, whence + 'preserve:');
81
+ this.files.preserved.push(path);
82
+ meta.action = 'preserve';
83
+ whenify(meta, this.now());
84
+ meta.actions.push(meta.action);
85
+ this.audit.push([CN + FN + wstr + meta.action,
86
+ { ...meta, action: meta.action, path }]);
87
+ }
88
+ }
89
+ if (existing.write && !protect) {
90
+ write = true;
91
+ }
92
+ else if (existing.present) {
93
+ if (oldcontent.length !== content.length || oldcontent !== content) {
94
+ let newpath = node_path_1.default.join(folder, node_path_1.default.basename(path).replace(/\.[^.]+$/, '') +
95
+ '.new' + node_path_1.default.extname(path));
96
+ this.saveFile(newpath, content, { flush: true }, whence + 'present:');
97
+ this.files.presented.push(path);
98
+ meta.action = 'present';
99
+ whenify(meta, this.now());
100
+ meta.actions.push(meta.action);
101
+ this.audit.push([CN + FN + wstr + meta.action,
102
+ { ...meta, action: meta.action, path }]);
103
+ }
104
+ }
105
+ if (!protect) {
106
+ if (existing.diff) {
107
+ write = false;
108
+ if (oldcontent.length !== content.length || oldcontent !== content) {
109
+ meta.action = 'diff';
110
+ const cstr = 'string' === typeof content ? content : content.toString('utf8');
111
+ const diffcontent = this.diff(cstr, oldcontent.toString());
112
+ this.saveFile(path, diffcontent, { encoding: 'utf8' }, whence + meta.action, content);
113
+ this.files.diffed.push(path);
114
+ const conflict = cstr !== diffcontent;
115
+ if (conflict) {
116
+ this.files.conflicted.push(path);
117
+ }
118
+ whenify(meta, this.now());
119
+ meta.actions.push(meta.action);
120
+ meta.conflict = conflict;
121
+ this.audit.push([CN + FN + wstr + meta.action,
122
+ { ...meta, action: meta.action, path }]);
123
+ }
124
+ }
125
+ else if (existing.merge) {
126
+ write = false;
127
+ if (oldcontent.length !== content.length || oldcontent !== content) {
128
+ const cstr = 'string' === typeof content ? content : content.toString('utf8');
129
+ if (this.duplicate) {
130
+ const dfolder = this.duplicateFolder();
131
+ const dpath = node_path_1.default.join(dfolder, path);
132
+ if (this.existsFile(dpath)) {
133
+ meta.action = 'merge';
134
+ const origcontent = this.loadFile(dpath, { encoding: 'utf8' });
135
+ const mergeres = this.merge(cstr, oldcontent.toString(), origcontent);
136
+ const diffcontent = mergeres.content;
137
+ const conflict = mergeres.conflict;
138
+ this.saveFile(path, diffcontent, { encoding: 'utf8' }, whence + meta.action, content);
139
+ this.files.merged.push(path);
140
+ if (conflict) {
141
+ this.files.conflicted.push(path);
142
+ }
143
+ whenify(meta, this.now());
144
+ meta.actions.push(meta.action);
145
+ meta.conflict = conflict;
146
+ this.audit.push([CN + FN + wstr + meta.action,
147
+ { ...meta, action: meta.action, path }]);
148
+ }
149
+ }
150
+ }
151
+ else {
152
+ this.files.unchanged.push(path);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ if (write) {
158
+ meta.action = 'write';
159
+ this.saveFile(path, content, whence + meta.action);
160
+ this.files.written.push(path);
161
+ meta.actions.push(meta.action);
162
+ whenify(meta, this.now());
163
+ this.audit.push([CN + FN + wstr + meta.action,
164
+ { ...meta, action: meta.action, path }]);
165
+ }
166
+ this.addmeta(path, meta);
167
+ }
168
+ copy(frompath, topath, write, whence) {
169
+ const wstr = null == whence ? '' : whence + ':';
170
+ const FN = 'copy:';
171
+ if ('string' === typeof write) {
172
+ whence = write;
173
+ write = false;
174
+ }
175
+ else if (null == write) {
176
+ write = false;
177
+ }
178
+ whence = wstr + FN;
179
+ const isBinary = (0, basic_1.isbinext)(frompath);
180
+ const content = this.loadFile(frompath, { encoding: isBinary ? null : 'utf8' }, whence);
181
+ this.save(topath, content, whence);
182
+ }
183
+ // TODO: need to record if diffed, merged
184
+ merge(newcontent, oldcontent, origcontent) {
185
+ const isowhen = new Date(this.when).toISOString();
186
+ const isolast = new Date(this.last()).toISOString();
187
+ // Consider the previously generated pure version, stored in
188
+ // .jostraca/generated to be the "original". That preserves
189
+ // manual edits in the main generated output.
190
+ const diffres = Diff3.merge(oldcontent, origcontent, newcontent, {
191
+ stringSeparator: '\n',
192
+ excludeFalseConflicts: true,
193
+ label: {
194
+ a: 'EXISTING: ' + isolast,
195
+ b: 'GENERATED: ' + isowhen,
196
+ }
197
+ });
198
+ // if (oldcontent.includes('foo js')) {
199
+ // console.log(
200
+ // 'DIFF======',
201
+ // '\no=', JSON.stringify(oldcontent),
202
+ // '\ng=', JSON.stringify(origcontent),
203
+ // '\nn=', JSON.stringify(newcontent),
204
+ // diffres
205
+ // )
206
+ // }
207
+ const conflict = diffres.conflict;
208
+ const content = diffres.result.join('\n');
209
+ return { content, conflict };
210
+ }
211
+ diff(oldcontent, newcontent) {
212
+ const isowhen = new Date(this.when).toISOString();
213
+ const isolast = new Date(this.last()).toISOString();
214
+ const difflines = Diff.diffLines(newcontent, oldcontent);
215
+ const out = [];
216
+ difflines.forEach((part) => {
217
+ if (part.added) {
218
+ out.push('<<<<<<< GENERATED: ' + isowhen + '\n');
219
+ out.push(part.value);
220
+ out.push('>>>>>>> GENERATED: ' + isowhen + '\n');
221
+ }
222
+ else if (part.removed) {
223
+ out.push('<<<<<<< EXISTING: ' + isolast + '\n');
224
+ out.push(part.value);
225
+ out.push('>>>>>>> EXISTING: ' + isolast + '\n');
226
+ }
227
+ else {
228
+ out.push(part.value);
229
+ }
230
+ });
231
+ const content = out.join('');
232
+ return content;
233
+ }
234
+ existsFile(path, whence) {
235
+ const when = this.now();
236
+ const wstr = null == whence ? '' : whence + ':';
237
+ const fs = this.fs();
238
+ const FN = 'existsFile:';
239
+ validPath(path, this.maxdepth, CN + FN + 'from:' + wstr);
240
+ const fullpath = node_path_1.default.isAbsolute(path) ? path : node_path_1.default.join(this.folder, path);
241
+ try {
242
+ const exists = fs.existsSync(fullpath);
243
+ this.audit.push([CN + FN + wstr,
244
+ { path, when, exists }]);
245
+ return exists;
246
+ }
247
+ catch (err) {
248
+ this.audit.push(['ERROR:' + CN + FN + wstr,
249
+ { path, when, err }]);
250
+ err.message = CN + FN + wstr + ' path=' + path +
251
+ ' err=' + err.message;
252
+ throw err;
253
+ }
254
+ }
255
+ copyFile(frompath, topath, whence) {
256
+ const when = this.now();
257
+ const wstr = null == whence ? '' : whence + ':';
258
+ const fs = this.fs();
259
+ const FN = 'copyFile:';
260
+ validPath(frompath, this.maxdepth, CN + FN + 'from:' + wstr);
261
+ validPath(topath, this.maxdepth, CN + FN + 'to:' + wstr);
262
+ const isBinary = (0, basic_1.isbinext)(frompath);
263
+ const fulltopath = node_path_1.default.isAbsolute(topath) ? topath : node_path_1.default.join(this.folder, topath);
264
+ const fullfrompath = node_path_1.default.isAbsolute(frompath) ? frompath : node_path_1.default.join(this.folder, frompath);
265
+ try {
266
+ const existed = fs.existsSync(fulltopath);
267
+ fs.mkdirSync(node_path_1.default.dirname(fulltopath), { recursive: true });
268
+ const content = fs.readFileSync(fullfrompath, isBinary ? undefined : 'utf8');
269
+ fs.writeFileSync(topath, content, { flush: true });
270
+ this.audit.push([CN + FN + wstr,
271
+ { topath, frompath, when, existed, size: content.length }]);
272
+ }
273
+ catch (err) {
274
+ this.audit.push(['ERROR:' + CN + FN + wstr,
275
+ { topath, frompath, when, err }]);
276
+ err.message = CN + FN + wstr + ' topath=' + topath + ' frompath=' + frompath +
277
+ ' err=' + err.message;
278
+ throw err;
279
+ }
280
+ }
281
+ loadJSON(path, opts, whence) {
282
+ const when = this.now();
283
+ const wstr = null == whence ? '' : whence + ':';
284
+ const FN = 'loadJSON:';
285
+ if ('string' === typeof opts) {
286
+ whence = opts;
287
+ opts = {};
288
+ }
289
+ else {
290
+ opts = opts || {};
291
+ }
292
+ opts.encoding = opts.encoding || 'utf8';
293
+ try {
294
+ const content = this.loadFile(path, opts, whence);
295
+ const cstr = 'string' === typeof content ? content : content.toString(opts.encoding);
296
+ const json = JSON.parse(cstr);
297
+ this.audit.push([CN + FN + wstr,
298
+ { path, when, size: content.length }]);
299
+ return json;
300
+ }
301
+ catch (err) {
302
+ this.audit.push(['ERROR:' + CN + FN + wstr,
303
+ { path, when, err }]);
304
+ err.message = CN + FN + wstr + ' path=' + path + ' err=' + err.message;
305
+ throw err;
306
+ }
307
+ }
308
+ saveJSON(path, json, opts, whence) {
309
+ const when = this.now();
310
+ const wstr = null == whence ? '' : whence + ':';
311
+ const FN = 'saveJSON:';
312
+ if ('string' === typeof opts) {
313
+ whence = opts;
314
+ opts = {};
315
+ }
316
+ else {
317
+ opts = opts || {};
318
+ }
319
+ opts.encoding = opts.encoding || 'utf8';
320
+ try {
321
+ const jstr = 'string' === typeof json ? json : JSON.stringify(json, null, 2);
322
+ this.saveFile(path, jstr, opts, whence);
323
+ this.audit.push([CN + FN + wstr,
324
+ { path, when, size: jstr.length }]);
325
+ return jstr;
326
+ }
327
+ catch (err) {
328
+ this.audit.push(['ERROR:' + CN + FN + wstr,
329
+ { path, when, err }]);
330
+ err.message = CN + FN + wstr + ' path=' + path + ' err=' + err.message;
331
+ throw err;
332
+ }
333
+ }
334
+ loadFile(path, opts, whence) {
335
+ const when = this.now();
336
+ const wstr = null == whence ? '' : whence + ':';
337
+ const fs = this.fs();
338
+ const FN = 'loadFile:';
339
+ if ('string' === typeof opts) {
340
+ whence = opts;
341
+ opts = {};
342
+ }
343
+ else {
344
+ opts = opts || {};
345
+ }
346
+ opts.encoding = undefined === opts.encoding ? 'utf8' : opts.encoding;
347
+ validPath(path, this.maxdepth, CN + FN + wstr);
348
+ try {
349
+ const fullpath = node_path_1.default.isAbsolute(path) ? path : node_path_1.default.join(this.folder, path);
350
+ const content = fs.readFileSync(fullpath, opts);
351
+ this.audit.push([CN + FN + wstr,
352
+ { path, when, size: content.length }]);
353
+ return content;
354
+ }
355
+ catch (err) {
356
+ this.audit.push(['ERROR:' + CN + FN + wstr,
357
+ { path, when, err }]);
358
+ err.message = CN + FN + wstr + ' path=' + path + ' err=' + err.message;
359
+ throw err;
360
+ }
361
+ }
362
+ saveFile(path, content, opts, whence, original) {
363
+ const when = this.now();
364
+ const wstr = null == whence ? '' : whence + ':';
365
+ const fs = this.fs();
366
+ const FN = 'saveFile:';
367
+ if ('string' === typeof opts) {
368
+ whence = opts;
369
+ opts = {};
370
+ }
371
+ else {
372
+ opts = opts || {};
373
+ }
374
+ opts.encoding = opts.encoding || ('string' === typeof content ? 'utf8' : undefined);
375
+ validPath(path, this.maxdepth, FN);
376
+ if ('string' !== typeof content && !(content instanceof Buffer)) {
377
+ throw new Error(CN + FN + wstr + ' invalid content, path=' + path +
378
+ ' content=' + content);
379
+ }
380
+ try {
381
+ path = node_path_1.default.normalize(path);
382
+ const isAbsolute = node_path_1.default.isAbsolute(path);
383
+ const fullpath = isAbsolute ? path : node_path_1.default.join(this.folder, path);
384
+ const parentfolder = node_path_1.default.dirname(fullpath);
385
+ const existed = fs.existsSync(fullpath);
386
+ fs.mkdirSync(parentfolder, { recursive: true });
387
+ fs.writeFileSync(fullpath, content, opts);
388
+ this.audit.push([CN + FN + wstr,
389
+ { path, when, existed, size: content.length }]);
390
+ const withinFolder = path.startsWith(this.folder);
391
+ if (this.duplicate &&
392
+ (!isAbsolute || withinFolder) &&
393
+ (node_path_1.default.basename(path) !== this.metafile())) {
394
+ const dfolder = this.duplicateFolder();
395
+ const dpath = node_path_1.default.join(dfolder, path);
396
+ fs.mkdirSync(node_path_1.default.dirname(dpath), { recursive: true });
397
+ const dopts = { ...opts, flush: true };
398
+ const dcontent = null == original ? content : original;
399
+ fs.writeFileSync(dpath, dcontent, dopts);
400
+ }
401
+ }
402
+ catch (err) {
403
+ this.audit.push(['ERROR:' + CN + FN + wstr,
404
+ { path, when, size: content.length, err }]);
405
+ err.message = CN + FN + wstr + ' path=' + path + ':' + err.message;
406
+ throw err;
407
+ }
408
+ }
409
+ }
410
+ exports.FileHandler = FileHandler;
411
+ function whenify(meta, now) {
412
+ meta.when = now;
413
+ meta.hwhen = (0, basic_1.humanify)(now);
414
+ }
415
+ function validPath(path, maxdepth, errmark) {
416
+ if (null == path || '' == path || 'string' !== typeof path) {
417
+ throw new Error('ERROR:' + errmark + ' invalid path, path=' + path);
418
+ }
419
+ const depth = node_path_1.default.normalize(node_path_1.default.dirname(path)).split(node_path_1.default.sep).filter(Boolean).length;
420
+ if (maxdepth < depth) {
421
+ throw new Error(errmark + ' path too deep, path=' + path);
422
+ }
423
+ }
424
+ //# sourceMappingURL=FileHandler.js.map