mem-fs-editor 8.1.2 → 9.1.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/README.md +2 -0
- package/lib/actions/append-tpl.js +2 -2
- package/lib/actions/append.js +3 -3
- package/lib/actions/commit-file-async.js +17 -13
- package/lib/actions/commit.js +14 -20
- package/lib/actions/copy-async.js +29 -33
- package/lib/actions/copy-tpl-async.js +11 -26
- package/lib/actions/copy-tpl.js +21 -10
- package/lib/actions/copy.js +29 -22
- package/lib/actions/delete.js +14 -15
- package/lib/actions/dump.js +26 -0
- package/lib/actions/exists.js +2 -2
- package/lib/actions/extend-json.js +3 -3
- package/lib/actions/read.js +2 -2
- package/lib/actions/write-json.js +2 -2
- package/lib/actions/write.js +7 -7
- package/lib/index.js +5 -1
- package/lib/state.js +75 -0
- package/lib/transform.js +13 -0
- package/lib/util.js +26 -26
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -82,6 +82,8 @@ Optionally, pass an `options.process` function (`process(contents)`) returning a
|
|
|
82
82
|
|
|
83
83
|
Optionally, when `from` is a glob pattern, pass an `options.processDestinationPath` function (`processDestinationPath(destinationFile)`) returning a string who'll become the new file name.
|
|
84
84
|
|
|
85
|
+
`options.noGlob` can be used to by bypass glob matching entirely. In that case, `from` will directly match file paths against the file system.
|
|
86
|
+
|
|
85
87
|
### `#copyAsync(from, to, [options], context[, templateOptions ])`
|
|
86
88
|
|
|
87
89
|
Async version of `copy`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const ejs = require('ejs');
|
|
4
4
|
|
|
5
5
|
module.exports = function (to, contents, context, tplSettings, options) {
|
|
6
6
|
context = context || {};
|
|
@@ -11,7 +11,7 @@ module.exports = function (to, contents, context, tplSettings, options) {
|
|
|
11
11
|
ejs.render(
|
|
12
12
|
contents.toString(),
|
|
13
13
|
context,
|
|
14
|
-
tplSettings
|
|
14
|
+
tplSettings,
|
|
15
15
|
),
|
|
16
16
|
options);
|
|
17
17
|
};
|
package/lib/actions/append.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const {EOL} = require('os');
|
|
4
4
|
|
|
5
5
|
module.exports = function (to, contents, options) {
|
|
6
6
|
options = {
|
|
7
7
|
trimEnd: true,
|
|
8
8
|
separator: EOL,
|
|
9
|
-
...options
|
|
9
|
+
...options,
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
if (!this.exists(to) && options.create) {
|
|
@@ -14,7 +14,7 @@ module.exports = function (to, contents, options) {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
let currentContents = this.read(to);
|
|
18
18
|
if (options.trimEnd) {
|
|
19
19
|
currentContents = currentContents.replace(/\s+$/, '');
|
|
20
20
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const fs = require('fs').promises;
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const {clearFileState, isFileStateModified, isFileStateDeleted, setCommittedFile} = require('../state');
|
|
5
6
|
|
|
6
7
|
async function write(file) {
|
|
7
|
-
|
|
8
|
+
const dir = path.dirname(file.path);
|
|
8
9
|
try {
|
|
9
10
|
if (!(await fs.stat(dir)).isDirectory()) {
|
|
10
11
|
throw new Error(`${file.path} is not a directory`);
|
|
@@ -17,25 +18,28 @@ async function write(file) {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const options = {};
|
|
22
|
+
if (file.stat) {
|
|
23
|
+
options.mode = file.stat.mode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
await fs.writeFile(file.path, file.contents, options);
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
async function remove(file) {
|
|
26
|
-
|
|
30
|
+
const remove = fs.rm || fs.rmdir;
|
|
31
|
+
await remove(file.path, {recursive: true});
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
module.exports = async function (file) {
|
|
30
35
|
this.store.add(file);
|
|
31
|
-
if (file
|
|
32
|
-
file
|
|
36
|
+
if (isFileStateModified(file)) {
|
|
37
|
+
setCommittedFile(file);
|
|
33
38
|
await write(file);
|
|
34
|
-
} else if (file
|
|
35
|
-
file
|
|
39
|
+
} else if (isFileStateDeleted(file)) {
|
|
40
|
+
setCommittedFile(file);
|
|
36
41
|
await remove(file);
|
|
37
42
|
}
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
delete file.isNew;
|
|
44
|
+
clearFileState(file);
|
|
41
45
|
};
|
package/lib/actions/commit.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const {
|
|
3
|
+
const {promisify} = require('util');
|
|
4
|
+
const {pipeline: _pipeline} = require('stream');
|
|
5
|
+
const pipeline = promisify(_pipeline);
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
var self = this;
|
|
7
|
+
const {createPendingFilesPassthrough, createCommitTransform} = require('../transform');
|
|
8
8
|
|
|
9
|
+
module.exports = function (filters, stream, cb) {
|
|
9
10
|
if (typeof filters === 'function') {
|
|
10
11
|
cb = filters;
|
|
11
12
|
filters = [];
|
|
@@ -16,24 +17,17 @@ module.exports = function (filters, stream, cb) {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
stream = stream || this.store.stream();
|
|
19
|
-
|
|
20
|
-
// Don't process deleted file who haven't been commited yet.
|
|
21
|
-
if (file.state === 'modified' || (file.state === 'deleted' && !file.isNew)) {
|
|
22
|
-
this.push(file);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
cb();
|
|
26
|
-
});
|
|
20
|
+
filters = filters || [];
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
self.commitFileAsync(file).then(() => cb()).catch(error => cb(error));
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
pipeline(
|
|
22
|
+
const promise = pipeline(
|
|
33
23
|
stream,
|
|
34
|
-
|
|
24
|
+
createPendingFilesPassthrough(),
|
|
35
25
|
...filters,
|
|
36
|
-
|
|
37
|
-
(...args) => cb(...args)
|
|
26
|
+
createCommitTransform(this),
|
|
38
27
|
);
|
|
28
|
+
if (cb) {
|
|
29
|
+
return promise.then(() => cb(), cb);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return promise;
|
|
39
33
|
};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
async function applyProcessingFileFunc(processFile, filename
|
|
14
|
-
|
|
15
|
-
return output
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const fsPromises = require('fs').promises;
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const globby = require('globby');
|
|
8
|
+
const multimatch = require('multimatch');
|
|
9
|
+
const ejs = require('ejs');
|
|
10
|
+
const util = require('../util');
|
|
11
|
+
const normalize = require('normalize-path');
|
|
12
|
+
|
|
13
|
+
async function applyProcessingFileFunc(processFile, filename) {
|
|
14
|
+
const output = await Promise.resolve(processFile.call(this, filename));
|
|
15
|
+
return Buffer.isBuffer(output) ? output : Buffer.from(output);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function renderFilepath(filepath, context, tplSettings) {
|
|
@@ -24,19 +24,19 @@ function renderFilepath(filepath, context, tplSettings) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async function getOneFile(from) {
|
|
27
|
-
|
|
27
|
+
let oneFile;
|
|
28
28
|
if (typeof from === 'string') {
|
|
29
29
|
oneFile = from;
|
|
30
30
|
} else {
|
|
31
31
|
return undefined;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
const resolved = path.resolve(oneFile);
|
|
35
35
|
try {
|
|
36
36
|
if ((await fsPromises.stat(resolved)).isFile()) {
|
|
37
37
|
return resolved;
|
|
38
38
|
}
|
|
39
|
-
} catch (
|
|
39
|
+
} catch (_) {
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return undefined;
|
|
@@ -45,16 +45,16 @@ async function getOneFile(from) {
|
|
|
45
45
|
exports.copyAsync = async function (from, to, options, context, tplSettings) {
|
|
46
46
|
to = path.resolve(to);
|
|
47
47
|
options = options || {};
|
|
48
|
-
|
|
48
|
+
const oneFile = await getOneFile(from);
|
|
49
49
|
if (oneFile) {
|
|
50
50
|
return this._copySingleAsync(oneFile, renderFilepath(to, context, tplSettings), options);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
const fromGlob = util.globify(from);
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
const globOptions = {...options.globOptions, nodir: true};
|
|
56
|
+
const diskFiles = globby.sync(fromGlob, globOptions).map(filepath => path.resolve(filepath));
|
|
57
|
+
const storeFiles = [];
|
|
58
58
|
this.store.each(file => {
|
|
59
59
|
// The store may have a glob path and when we try to copy it will fail because not real file
|
|
60
60
|
if (!globby.hasMagic(normalize(file.path)) && multimatch([file.path], fromGlob).length !== 0 && !diskFiles.includes(file.path)) {
|
|
@@ -62,17 +62,17 @@ exports.copyAsync = async function (from, to, options, context, tplSettings) {
|
|
|
62
62
|
}
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
let generateDestination = () => to;
|
|
66
66
|
if (Array.isArray(from) || !this.exists(from) || globby.hasMagic(normalize(from))) {
|
|
67
67
|
assert(
|
|
68
68
|
!this.exists(to) || fs.statSync(to).isDirectory(),
|
|
69
|
-
'When copying multiple files, provide a directory as destination'
|
|
69
|
+
'When copying multiple files, provide a directory as destination',
|
|
70
70
|
);
|
|
71
71
|
|
|
72
72
|
const processDestinationPath = options.processDestinationPath || (path => path);
|
|
73
|
-
|
|
73
|
+
const root = util.getCommonPath(from);
|
|
74
74
|
generateDestination = filepath => {
|
|
75
|
-
|
|
75
|
+
const toFile = path.relative(root, filepath);
|
|
76
76
|
return processDestinationPath(path.join(to, toFile));
|
|
77
77
|
};
|
|
78
78
|
}
|
|
@@ -81,12 +81,8 @@ exports.copyAsync = async function (from, to, options, context, tplSettings) {
|
|
|
81
81
|
assert(options.ignoreNoMatch || diskFiles.length > 0 || storeFiles.length > 0, 'Trying to copy from a source that does not exist: ' + from);
|
|
82
82
|
|
|
83
83
|
await Promise.all([
|
|
84
|
-
...diskFiles.map(file =>
|
|
85
|
-
|
|
86
|
-
}),
|
|
87
|
-
...storeFiles.map(file => {
|
|
88
|
-
return Promise.resolve(this._copySingle(file, renderFilepath(generateDestination(file), context, tplSettings), options));
|
|
89
|
-
})
|
|
84
|
+
...diskFiles.map(file => this._copySingleAsync(file, renderFilepath(generateDestination(file), context, tplSettings), options)),
|
|
85
|
+
...storeFiles.map(file => Promise.resolve(this._copySingle(file, renderFilepath(generateDestination(file), context, tplSettings), options))),
|
|
90
86
|
]);
|
|
91
87
|
};
|
|
92
88
|
|
|
@@ -95,7 +91,7 @@ exports._copySingleAsync = async function (from, to, options = {}) {
|
|
|
95
91
|
return this._copySingle(from, to, options);
|
|
96
92
|
}
|
|
97
93
|
|
|
98
|
-
|
|
94
|
+
const contents = await applyProcessingFileFunc.call(this, options.processFile, from);
|
|
99
95
|
|
|
100
96
|
if (options.append) {
|
|
101
97
|
if (!this.store.existsInMemory) {
|
|
@@ -108,6 +104,6 @@ exports._copySingleAsync = async function (from, to, options = {}) {
|
|
|
108
104
|
}
|
|
109
105
|
}
|
|
110
106
|
|
|
111
|
-
|
|
107
|
+
const stat = await fsPromises.stat(from);
|
|
112
108
|
this.write(to, contents, stat);
|
|
113
109
|
};
|
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const {render} = require('../util');
|
|
7
|
-
|
|
8
|
-
async function renderFile(filename, context, tplSettings) {
|
|
9
|
-
let result;
|
|
10
|
-
|
|
11
|
-
if (await isBinaryFile(filename)) {
|
|
12
|
-
result = await fs.readFile(filename);
|
|
13
|
-
} else {
|
|
14
|
-
result = await ejs.renderFile(filename, context, tplSettings);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return result;
|
|
18
|
-
}
|
|
3
|
+
const ejs = require('ejs');
|
|
4
|
+
const fs = require('fs').promises;
|
|
5
|
+
const {isBinary} = require('../util');
|
|
19
6
|
|
|
20
7
|
module.exports = async function (from, to, context, tplSettings, options) {
|
|
21
8
|
context = context || {};
|
|
@@ -27,18 +14,16 @@ module.exports = async function (from, to, context, tplSettings, options) {
|
|
|
27
14
|
{
|
|
28
15
|
processDestinationPath: path => path.replace(/.ejs$/, ''),
|
|
29
16
|
...options,
|
|
30
|
-
|
|
31
|
-
|
|
17
|
+
async processFile(filename) {
|
|
18
|
+
if (isBinary(filename, null)) {
|
|
19
|
+
return fs.readFile(filename);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return ejs.renderFile(filename, context, tplSettings);
|
|
32
23
|
},
|
|
33
|
-
process:
|
|
34
|
-
return render(contents, context, {
|
|
35
|
-
// Setting filename by default allow including partials.
|
|
36
|
-
filename: filename,
|
|
37
|
-
...tplSettings
|
|
38
|
-
});
|
|
39
|
-
}
|
|
24
|
+
process: (contents, filename, destination) => this._processTpl({contents, filename, destination, context, tplSettings}),
|
|
40
25
|
},
|
|
41
26
|
context,
|
|
42
|
-
tplSettings
|
|
27
|
+
tplSettings,
|
|
43
28
|
);
|
|
44
29
|
};
|
package/lib/actions/copy-tpl.js
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const ejs = require('ejs');
|
|
4
|
+
const {isBinary} = require('../util');
|
|
4
5
|
|
|
5
|
-
module.exports = function (
|
|
6
|
+
module.exports._processTpl = function ({contents, filename, context, tplSettings}) {
|
|
7
|
+
if (isBinary(filename, contents)) {
|
|
8
|
+
return contents;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return ejs.render(
|
|
12
|
+
contents.toString(),
|
|
13
|
+
context,
|
|
14
|
+
{
|
|
15
|
+
// Setting filename by default allow including partials.
|
|
16
|
+
filename,
|
|
17
|
+
...tplSettings,
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
module.exports.copyTpl = function (from, to, context, tplSettings, options) {
|
|
6
23
|
context = context || {};
|
|
7
24
|
tplSettings = tplSettings || {};
|
|
8
25
|
|
|
@@ -12,15 +29,9 @@ module.exports = function (from, to, context, tplSettings, options) {
|
|
|
12
29
|
{
|
|
13
30
|
processDestinationPath: path => path.replace(/.ejs$/, ''),
|
|
14
31
|
...options,
|
|
15
|
-
process:
|
|
16
|
-
return render(contents, context, {
|
|
17
|
-
// Setting filename by default allow including partials.
|
|
18
|
-
filename: filename,
|
|
19
|
-
...tplSettings
|
|
20
|
-
});
|
|
21
|
-
}
|
|
32
|
+
process: (contents, filename) => this._processTpl({contents, filename, context, tplSettings}),
|
|
22
33
|
},
|
|
23
34
|
context,
|
|
24
|
-
tplSettings
|
|
35
|
+
tplSettings,
|
|
25
36
|
);
|
|
26
37
|
};
|
package/lib/actions/copy.js
CHANGED
|
@@ -1,46 +1,53 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const globby = require('globby');
|
|
7
|
+
const multimatch = require('multimatch');
|
|
8
|
+
const ejs = require('ejs');
|
|
9
|
+
const util = require('../util');
|
|
10
|
+
const normalize = require('normalize-path');
|
|
11
11
|
|
|
12
12
|
function applyProcessingFunc(process, contents, filename) {
|
|
13
|
-
|
|
14
|
-
return output
|
|
13
|
+
const output = process(contents, filename);
|
|
14
|
+
return Buffer.isBuffer(output) ? output : Buffer.from(output);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
exports.copy = function (from, to, options, context, tplSettings) {
|
|
18
18
|
to = path.resolve(to);
|
|
19
19
|
options = options || {};
|
|
20
|
-
|
|
20
|
+
const fromGlob = util.globify(from);
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
let diskFiles = [];
|
|
23
|
+
if (options.noGlob) {
|
|
24
|
+
const fromFiles = Array.isArray(fromGlob) ? fromGlob : [fromGlob];
|
|
25
|
+
diskFiles = fromFiles.filter(filepath => fs.existsSync(filepath));
|
|
26
|
+
} else {
|
|
27
|
+
const globOptions = {...options.globOptions, nodir: true};
|
|
28
|
+
diskFiles = globby.sync(fromGlob, globOptions).map(file => path.resolve(file));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const storeFiles = [];
|
|
25
32
|
this.store.each(file => {
|
|
26
33
|
// The store may have a glob path and when we try to copy it will fail because not real file
|
|
27
34
|
if (!globby.hasMagic(normalize(file.path)) && multimatch([file.path], fromGlob).length !== 0 && !diskFiles.includes(file.path)) {
|
|
28
35
|
storeFiles.push(file.path);
|
|
29
36
|
}
|
|
30
37
|
});
|
|
31
|
-
|
|
38
|
+
const files = diskFiles.concat(storeFiles);
|
|
32
39
|
|
|
33
|
-
|
|
40
|
+
let generateDestination = () => to;
|
|
34
41
|
if (Array.isArray(from) || !this.exists(from) || globby.hasMagic(normalize(from))) {
|
|
35
42
|
assert(
|
|
36
43
|
!this.exists(to) || fs.statSync(to).isDirectory(),
|
|
37
|
-
'When copying multiple files, provide a directory as destination'
|
|
44
|
+
'When copying multiple files, provide a directory as destination',
|
|
38
45
|
);
|
|
39
46
|
|
|
40
47
|
const processDestinationPath = options.processDestinationPath || (path => path);
|
|
41
|
-
|
|
48
|
+
const root = util.getCommonPath(from);
|
|
42
49
|
generateDestination = filepath => {
|
|
43
|
-
|
|
50
|
+
const toFile = path.relative(root, filepath);
|
|
44
51
|
return processDestinationPath(path.join(to, toFile));
|
|
45
52
|
};
|
|
46
53
|
}
|
|
@@ -49,7 +56,7 @@ exports.copy = function (from, to, options, context, tplSettings) {
|
|
|
49
56
|
assert(options.ignoreNoMatch || files.length > 0, 'Trying to copy from a source that does not exist: ' + from);
|
|
50
57
|
|
|
51
58
|
files.forEach(file => {
|
|
52
|
-
|
|
59
|
+
let toFile = generateDestination(file);
|
|
53
60
|
if (context) {
|
|
54
61
|
toFile = ejs.render(toFile, context, tplSettings);
|
|
55
62
|
}
|
|
@@ -61,9 +68,9 @@ exports.copy = function (from, to, options, context, tplSettings) {
|
|
|
61
68
|
exports._copySingle = function (from, to, options = {}) {
|
|
62
69
|
assert(this.exists(from), 'Trying to copy from a source that does not exist: ' + from);
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
const file = this.store.get(from);
|
|
65
72
|
|
|
66
|
-
|
|
73
|
+
let {contents} = file;
|
|
67
74
|
if (options.process) {
|
|
68
75
|
contents = applyProcessingFunc(options.process, file.contents, file.path);
|
|
69
76
|
}
|
package/lib/actions/delete.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const globby = require('globby');
|
|
5
|
+
const multimatch = require('multimatch');
|
|
6
|
+
const util = require('../util');
|
|
7
|
+
const {setDeletedFileState} = require('../state');
|
|
7
8
|
|
|
8
9
|
function deleteFile(path, store) {
|
|
9
|
-
|
|
10
|
-
file
|
|
10
|
+
const file = store.get(path);
|
|
11
|
+
setDeletedFileState(file);
|
|
11
12
|
file.contents = null;
|
|
12
13
|
store.add(file);
|
|
13
14
|
}
|
|
@@ -17,21 +18,19 @@ module.exports = function (paths, options) {
|
|
|
17
18
|
paths = [paths];
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
paths = paths.map(
|
|
21
|
-
return path.resolve(filePath);
|
|
22
|
-
});
|
|
21
|
+
paths = paths.map(filePath => path.resolve(filePath));
|
|
23
22
|
paths = util.globify(paths);
|
|
24
23
|
options = options || {};
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
files.forEach(
|
|
25
|
+
const globOptions = options.globOptions || {};
|
|
26
|
+
const files = globby.sync(paths, globOptions);
|
|
27
|
+
files.forEach(file => {
|
|
29
28
|
deleteFile(file, this.store);
|
|
30
|
-
}
|
|
29
|
+
});
|
|
31
30
|
|
|
32
|
-
this.store.each(
|
|
31
|
+
this.store.each(file => {
|
|
33
32
|
if (multimatch([file.path], paths).length !== 0) {
|
|
34
33
|
deleteFile(file.path, this.store);
|
|
35
34
|
}
|
|
36
|
-
}
|
|
35
|
+
});
|
|
37
36
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const normalize = require('normalize-path');
|
|
5
|
+
|
|
6
|
+
const {hasClearedState, hasState, STATE, STATE_CLEARED} = require('../state');
|
|
7
|
+
|
|
8
|
+
module.exports = function (cwd = process.cwd(), filter = file => hasClearedState(file) || hasState(file)) {
|
|
9
|
+
return Object.fromEntries(
|
|
10
|
+
this.store.all().filter(file => filter(file)).map(file => {
|
|
11
|
+
const filePath = normalize(cwd ? path.relative(cwd, file.path) : file.path);
|
|
12
|
+
const fileDump = {
|
|
13
|
+
contents: file.contents ? file.contents.toString() : file.contents,
|
|
14
|
+
};
|
|
15
|
+
if (file[STATE]) {
|
|
16
|
+
fileDump[STATE] = file[STATE];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (file[STATE_CLEARED]) {
|
|
20
|
+
fileDump[STATE_CLEARED] = file[STATE_CLEARED];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return [filePath, fileDump];
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
};
|
package/lib/actions/exists.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const extend = require('deep-extend');
|
|
4
4
|
|
|
5
5
|
module.exports = function (filepath, contents, replacer, space) {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const originalContent = this.readJSON(filepath, {});
|
|
7
|
+
const newContent = extend({}, originalContent, contents);
|
|
8
8
|
|
|
9
9
|
this.writeJSON(filepath, newContent, replacer, space);
|
|
10
10
|
};
|
package/lib/actions/read.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module.exports = function (filepath, options) {
|
|
4
4
|
options = options || {raw: false};
|
|
5
|
-
|
|
5
|
+
const file = this.store.get(filepath);
|
|
6
6
|
|
|
7
|
-
if (file.
|
|
7
|
+
if (file.contents === null) {
|
|
8
8
|
if ('defaults' in options) {
|
|
9
9
|
return options.defaults;
|
|
10
10
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const DEFAULT_INDENTATION = 2;
|
|
4
4
|
|
|
5
5
|
module.exports = function (filepath, contents, replacer, space) {
|
|
6
|
-
|
|
6
|
+
const jsonStr = JSON.stringify(contents, replacer || null, space || DEFAULT_INDENTATION) + '\n';
|
|
7
7
|
|
|
8
8
|
return this.write(filepath, jsonStr);
|
|
9
9
|
};
|
package/lib/actions/write.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const assert = require('assert');
|
|
4
|
+
const {setModifiedFileState} = require('../state');
|
|
4
5
|
|
|
5
6
|
module.exports = function (filepath, contents, stat) {
|
|
6
7
|
assert(
|
|
7
|
-
typeof contents === 'string' || contents
|
|
8
|
-
'Expected `contents` to be a String or a Buffer'
|
|
8
|
+
typeof contents === 'string' || Buffer.isBuffer(contents),
|
|
9
|
+
'Expected `contents` to be a String or a Buffer',
|
|
9
10
|
);
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
file
|
|
13
|
-
file.
|
|
14
|
-
file.contents = typeof contents === 'string' ? Buffer.from(contents) : contents;
|
|
12
|
+
const file = this.store.get(filepath);
|
|
13
|
+
setModifiedFileState(file);
|
|
14
|
+
file.contents = Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
|
|
15
15
|
file.stat = stat;
|
|
16
16
|
this.store.add(file);
|
|
17
17
|
|
package/lib/index.js
CHANGED
|
@@ -15,14 +15,18 @@ EditionInterface.prototype.appendTpl = require('./actions/append-tpl.js');
|
|
|
15
15
|
EditionInterface.prototype.delete = require('./actions/delete.js');
|
|
16
16
|
EditionInterface.prototype.copy = require('./actions/copy.js').copy;
|
|
17
17
|
EditionInterface.prototype._copySingle = require('./actions/copy.js')._copySingle;
|
|
18
|
-
EditionInterface.prototype.copyTpl = require('./actions/copy-tpl.js');
|
|
18
|
+
EditionInterface.prototype.copyTpl = require('./actions/copy-tpl.js').copyTpl;
|
|
19
|
+
EditionInterface.prototype._processTpl = require('./actions/copy-tpl.js')._processTpl;
|
|
19
20
|
EditionInterface.prototype.copyAsync = require('./actions/copy-async.js').copyAsync;
|
|
20
21
|
EditionInterface.prototype._copySingleAsync = require('./actions/copy-async.js')._copySingleAsync;
|
|
21
22
|
EditionInterface.prototype.copyTplAsync = require('./actions/copy-tpl-async.js');
|
|
22
23
|
EditionInterface.prototype.move = require('./actions/move.js');
|
|
23
24
|
EditionInterface.prototype.commit = require('./actions/commit.js');
|
|
24
25
|
EditionInterface.prototype.commitFileAsync = require('./actions/commit-file-async.js');
|
|
26
|
+
EditionInterface.prototype.dump = require('./actions/dump.js');
|
|
25
27
|
|
|
26
28
|
exports.create = function (store) {
|
|
27
29
|
return new EditionInterface(store);
|
|
28
30
|
};
|
|
31
|
+
|
|
32
|
+
exports.State = require('./state');
|
package/lib/state.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
const STATE = 'state';
|
|
4
|
+
|
|
5
|
+
const STATE_CLEARED = 'stateCleared';
|
|
6
|
+
|
|
7
|
+
const STATE_MODIFIED = 'modified';
|
|
8
|
+
|
|
9
|
+
const STATE_DELETED = 'deleted';
|
|
10
|
+
|
|
11
|
+
const IS_NEW = 'isNew';
|
|
12
|
+
|
|
13
|
+
const setFileState = (file, state) => {
|
|
14
|
+
file[STATE] = state;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const isFileNew = file => {
|
|
18
|
+
if (file[IS_NEW] === undefined) {
|
|
19
|
+
file[IS_NEW] = !fs.existsSync(file.path);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return file[IS_NEW];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const isFileStateModified = file => file[STATE] === STATE_MODIFIED;
|
|
26
|
+
|
|
27
|
+
const setModifiedFileState = file => setFileState(file, STATE_MODIFIED);
|
|
28
|
+
|
|
29
|
+
const isFileStateDeleted = file => file[STATE] === STATE_DELETED;
|
|
30
|
+
|
|
31
|
+
const setDeletedFileState = file => setFileState(file, STATE_DELETED);
|
|
32
|
+
|
|
33
|
+
const isFilePending = file => isFileStateModified(file) || (isFileStateDeleted(file) && !isFileNew(file));
|
|
34
|
+
|
|
35
|
+
const setCommittedFile = file => {
|
|
36
|
+
file.committed = true;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const isFileCommitted = file => Boolean(file.committed);
|
|
40
|
+
|
|
41
|
+
const resetFileState = file => {
|
|
42
|
+
delete file[STATE];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const clearFileState = file => {
|
|
46
|
+
if (file[STATE]) {
|
|
47
|
+
file[STATE_CLEARED] = file[STATE];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
resetFileState(file);
|
|
51
|
+
delete file[IS_NEW];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const hasState = file => Boolean(file[STATE]);
|
|
55
|
+
|
|
56
|
+
const hasClearedState = file => Boolean(file[STATE_CLEARED]);
|
|
57
|
+
|
|
58
|
+
module.exports = {
|
|
59
|
+
STATE,
|
|
60
|
+
STATE_CLEARED,
|
|
61
|
+
STATE_MODIFIED,
|
|
62
|
+
STATE_DELETED,
|
|
63
|
+
isFileStateModified,
|
|
64
|
+
setModifiedFileState,
|
|
65
|
+
isFileStateDeleted,
|
|
66
|
+
setDeletedFileState,
|
|
67
|
+
setCommittedFile,
|
|
68
|
+
isFileCommitted,
|
|
69
|
+
isFileNew,
|
|
70
|
+
isFilePending,
|
|
71
|
+
resetFileState,
|
|
72
|
+
clearFileState,
|
|
73
|
+
hasState,
|
|
74
|
+
hasClearedState,
|
|
75
|
+
};
|
package/lib/transform.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const {createTransform} = require('./util');
|
|
2
|
+
const {isFilePending} = require('./state');
|
|
3
|
+
|
|
4
|
+
const createPendingFilesPassthrough = () => createTransform((file, _enc, cb) => {
|
|
5
|
+
// Don't process deleted file who haven't been commited yet.
|
|
6
|
+
cb(undefined, isFilePending(file) ? file : undefined);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const createCommitTransform = memFsEditor => createTransform((file, _enc, cb) => {
|
|
10
|
+
memFsEditor.commitFileAsync(file).then(() => cb()).catch(error => cb(error));
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
module.exports = {createPendingFilesPassthrough, createCommitTransform};
|
package/lib/util.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const commondir = require('commondir');
|
|
6
|
+
const globby = require('globby');
|
|
7
|
+
const normalize = require('normalize-path');
|
|
8
8
|
const {Transform} = require('stream');
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const {isBinaryFileSync} = require('isbinaryfile');
|
|
10
|
+
|
|
11
|
+
const {default: textextensions} = require('textextensions');
|
|
12
|
+
const {default: binaryextensions} = require('binaryextensions');
|
|
11
13
|
|
|
12
14
|
function notNullOrExclusion(file) {
|
|
13
15
|
return file != null && file.charAt(0) !== '!';
|
|
@@ -22,7 +24,7 @@ exports.getCommonPath = function (filePath) {
|
|
|
22
24
|
return commondir(filePath);
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
const globStartIndex = filePath.indexOf('*');
|
|
26
28
|
if (globStartIndex !== -1) {
|
|
27
29
|
filePath = filePath.substring(0, globStartIndex + 1);
|
|
28
30
|
} else if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
|
|
@@ -48,11 +50,11 @@ exports.globify = function (filePath) {
|
|
|
48
50
|
// entity on the disk is ambiguous. As such, match both files and directories.
|
|
49
51
|
return [
|
|
50
52
|
filePath,
|
|
51
|
-
normalize(path.join(filePath, '**'))
|
|
53
|
+
normalize(path.join(filePath, '**')),
|
|
52
54
|
];
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
const fsStats = fs.statSync(filePath);
|
|
56
58
|
if (fsStats.isFile()) {
|
|
57
59
|
return filePath;
|
|
58
60
|
}
|
|
@@ -67,27 +69,25 @@ exports.globify = function (filePath) {
|
|
|
67
69
|
exports.createTransform = function (transform) {
|
|
68
70
|
const stream = new Transform({
|
|
69
71
|
objectMode: true,
|
|
70
|
-
transform
|
|
72
|
+
transform(...args) {
|
|
71
73
|
return transform.apply(this, args);
|
|
72
|
-
}
|
|
74
|
+
},
|
|
73
75
|
});
|
|
74
76
|
return stream;
|
|
75
77
|
};
|
|
76
78
|
|
|
77
|
-
exports.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (isBinaryFileSync(contentsBuffer, contentsBuffer.length)) {
|
|
82
|
-
result = contentsBuffer;
|
|
83
|
-
} else {
|
|
84
|
-
result = ejs.render(
|
|
85
|
-
contents.toString(),
|
|
86
|
-
context,
|
|
87
|
-
tplSettings
|
|
88
|
-
);
|
|
79
|
+
exports.isBinary = (filePath, newFileContents) => {
|
|
80
|
+
const extension = path.extname(filePath).replace(/^\./, '') || path.basename(filePath);
|
|
81
|
+
if (binaryextensions.includes(extension)) {
|
|
82
|
+
return true;
|
|
89
83
|
}
|
|
90
84
|
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
if (textextensions.includes(extension)) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
93
88
|
|
|
89
|
+
return (
|
|
90
|
+
(fs.existsSync(filePath) && isBinaryFileSync(filePath))
|
|
91
|
+
|| (newFileContents && isBinaryFileSync(Buffer.isBuffer(newFileContents) ? newFileContents : Buffer.from(newFileContents)))
|
|
92
|
+
);
|
|
93
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mem-fs-editor",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.1.0",
|
|
4
4
|
"description": "File edition helpers working on top of mem-fs",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"fix": "eslint . --fix",
|
|
@@ -15,16 +15,18 @@
|
|
|
15
15
|
"lib"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
+
"binaryextensions": "^4.15.0",
|
|
18
19
|
"commondir": "^1.0.1",
|
|
19
20
|
"deep-extend": "^0.6.0",
|
|
20
21
|
"ejs": "^3.1.6",
|
|
21
|
-
"globby": "^11.0.
|
|
22
|
-
"isbinaryfile": "^4.0.
|
|
22
|
+
"globby": "^11.0.3",
|
|
23
|
+
"isbinaryfile": "^4.0.8",
|
|
23
24
|
"multimatch": "^5.0.0",
|
|
24
|
-
"normalize-path": "^3.0.0"
|
|
25
|
+
"normalize-path": "^3.0.0",
|
|
26
|
+
"textextensions": "^5.12.0"
|
|
25
27
|
},
|
|
26
28
|
"peerDependencies": {
|
|
27
|
-
"mem-fs": "^
|
|
29
|
+
"mem-fs": "^2.1.0"
|
|
28
30
|
},
|
|
29
31
|
"peerDependenciesMeta": {
|
|
30
32
|
"mem-fs": {
|
|
@@ -34,11 +36,11 @@
|
|
|
34
36
|
"devDependencies": {
|
|
35
37
|
"coveralls": "^3.0.3",
|
|
36
38
|
"escape-regexp": "0.0.1",
|
|
37
|
-
"eslint": "^
|
|
38
|
-
"eslint-config-xo-space": "^0.
|
|
39
|
-
"jest": "^
|
|
40
|
-
"mem-fs": "2.
|
|
41
|
-
"sinon": "^
|
|
39
|
+
"eslint": "^7.26.0",
|
|
40
|
+
"eslint-config-xo-space": "^0.28.0",
|
|
41
|
+
"jest": "^27.0.6",
|
|
42
|
+
"mem-fs": "^2.2.1",
|
|
43
|
+
"sinon": "^11.1.2"
|
|
42
44
|
},
|
|
43
45
|
"jest": {
|
|
44
46
|
"collectCoverage": true,
|