mem-fs-editor 9.2.0 → 9.5.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 +16 -6
- package/lib/actions/append-tpl.js +2 -9
- package/lib/actions/append.js +1 -1
- package/lib/actions/commit-file-async.js +16 -3
- package/lib/actions/commit.js +4 -4
- package/lib/actions/copy-async.js +41 -16
- package/lib/actions/copy-tpl-async.js +12 -6
- package/lib/actions/copy-tpl.js +12 -15
- package/lib/actions/copy.js +25 -15
- package/lib/actions/delete.js +4 -4
- package/lib/actions/dump.js +27 -15
- package/lib/actions/read-json.js +3 -1
- package/lib/actions/read.js +2 -2
- package/lib/actions/write-json.js +2 -1
- package/lib/actions/write.js +6 -6
- package/lib/index.js +2 -1
- package/lib/state.js +13 -12
- package/lib/transform.js +15 -10
- package/lib/util.js +25 -16
- package/package.json +10 -6
package/README.md
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
# mem-fs-editor
|
|
1
|
+
# mem-fs-editor
|
|
2
|
+
|
|
3
|
+
[](https://github.com/SBoudrias/mem-fs-editor/actions?query=workflow%3A%22Node.js+CI%22)
|
|
4
|
+
[](http://badge.fury.io/js/mem-fs-editor)
|
|
5
|
+
[](https://codecov.io/gh/SBoudrias/mem-fs-editor)
|
|
2
6
|
|
|
3
7
|
File edition helpers working on top of [mem-fs](https://github.com/SBoudrias/mem-fs)
|
|
4
8
|
|
|
5
9
|
## Usage
|
|
6
10
|
|
|
7
11
|
```js
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
import memFs from 'mem-fs';
|
|
13
|
+
import editor from 'mem-fs-editor';
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
const store = memFs.create();
|
|
16
|
+
const fs = editor.create(store);
|
|
13
17
|
|
|
14
|
-
fs.write(
|
|
18
|
+
fs.write('somefile.js', 'var a = 1;');
|
|
15
19
|
```
|
|
16
20
|
|
|
17
21
|
### `#read(filepath, [options])`
|
|
@@ -142,3 +146,9 @@ If provided, `filters` is an array of TransformStream to be applied on a stream
|
|
|
142
146
|
If provided, `stream` is a stream of vinyl files.
|
|
143
147
|
|
|
144
148
|
`callback` is called once the files are updated on disk.
|
|
149
|
+
|
|
150
|
+
### `#dump([cwd,] [filter])`
|
|
151
|
+
|
|
152
|
+
Dump files to compare expected result.
|
|
153
|
+
Provide a `cwd` for relative path. Allows to omit temporary path.
|
|
154
|
+
Provide a `filter` function or glob to focus on specific files.
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { render } = require('../util');
|
|
4
4
|
|
|
5
5
|
module.exports = function (to, contents, context, tplSettings, options) {
|
|
6
6
|
context = context || {};
|
|
7
7
|
tplSettings = tplSettings || {};
|
|
8
8
|
|
|
9
|
-
this.append(
|
|
10
|
-
to,
|
|
11
|
-
ejs.render(
|
|
12
|
-
contents.toString(),
|
|
13
|
-
context,
|
|
14
|
-
tplSettings,
|
|
15
|
-
),
|
|
16
|
-
options);
|
|
9
|
+
this.append(to, render(contents.toString(), context, tplSettings), options);
|
|
17
10
|
};
|
package/lib/actions/append.js
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs').promises;
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
clearFileState,
|
|
7
|
+
isFileStateModified,
|
|
8
|
+
isFileStateDeleted,
|
|
9
|
+
setCommittedFile,
|
|
10
|
+
} = require('../state');
|
|
6
11
|
|
|
7
12
|
async function write(file) {
|
|
8
13
|
const dir = path.dirname(file.path);
|
|
@@ -12,7 +17,7 @@ async function write(file) {
|
|
|
12
17
|
}
|
|
13
18
|
} catch (error) {
|
|
14
19
|
if (error.code === 'ENOENT') {
|
|
15
|
-
await fs.mkdir(dir, {recursive: true});
|
|
20
|
+
await fs.mkdir(dir, { recursive: true });
|
|
16
21
|
} else {
|
|
17
22
|
throw error;
|
|
18
23
|
}
|
|
@@ -24,11 +29,19 @@ async function write(file) {
|
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
await fs.writeFile(file.path, file.contents, options);
|
|
32
|
+
|
|
33
|
+
if (options.mode !== undefined) {
|
|
34
|
+
const { mode } = await fs.stat(file.path);
|
|
35
|
+
// eslint-disable-next-line no-bitwise
|
|
36
|
+
if ((mode & 0o777) !== (options.mode & 0o777)) {
|
|
37
|
+
await fs.chmod(file.path, options.mode);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
27
40
|
}
|
|
28
41
|
|
|
29
42
|
async function remove(file) {
|
|
30
43
|
const remove = fs.rm || fs.rmdir;
|
|
31
|
-
await remove(file.path, {recursive: true});
|
|
44
|
+
await remove(file.path, { recursive: true });
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
module.exports = async function (file) {
|
package/lib/actions/commit.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {promisify} = require('util');
|
|
4
|
-
const {pipeline: _pipeline} = require('stream');
|
|
3
|
+
const { promisify } = require('util');
|
|
4
|
+
const { pipeline: _pipeline } = require('stream');
|
|
5
5
|
const pipeline = promisify(_pipeline);
|
|
6
6
|
|
|
7
|
-
const {createPendingFilesPassthrough, createCommitTransform} = require('../transform');
|
|
7
|
+
const { createPendingFilesPassthrough, createCommitTransform } = require('../transform');
|
|
8
8
|
|
|
9
9
|
module.exports = function (filters, stream, cb) {
|
|
10
10
|
if (typeof filters === 'function') {
|
|
@@ -23,7 +23,7 @@ module.exports = function (filters, stream, cb) {
|
|
|
23
23
|
stream,
|
|
24
24
|
createPendingFilesPassthrough(),
|
|
25
25
|
...filters,
|
|
26
|
-
createCommitTransform(this)
|
|
26
|
+
createCommitTransform(this)
|
|
27
27
|
);
|
|
28
28
|
if (cb) {
|
|
29
29
|
return promise.then(() => cb(), cb);
|
|
@@ -6,7 +6,6 @@ const fsPromises = require('fs').promises;
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const globby = require('globby');
|
|
8
8
|
const multimatch = require('multimatch');
|
|
9
|
-
const ejs = require('ejs');
|
|
10
9
|
const util = require('../util');
|
|
11
10
|
const normalize = require('normalize-path');
|
|
12
11
|
|
|
@@ -20,7 +19,7 @@ function renderFilepath(filepath, context, tplSettings) {
|
|
|
20
19
|
return filepath;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
return
|
|
22
|
+
return util.render(filepath, context, tplSettings);
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
async function getOneFile(from) {
|
|
@@ -36,8 +35,7 @@ async function getOneFile(from) {
|
|
|
36
35
|
if ((await fsPromises.stat(resolved)).isFile()) {
|
|
37
36
|
return resolved;
|
|
38
37
|
}
|
|
39
|
-
} catch (_) {
|
|
40
|
-
}
|
|
38
|
+
} catch (_) {}
|
|
41
39
|
|
|
42
40
|
return undefined;
|
|
43
41
|
}
|
|
@@ -47,17 +45,27 @@ exports.copyAsync = async function (from, to, options, context, tplSettings) {
|
|
|
47
45
|
options = options || {};
|
|
48
46
|
const oneFile = await getOneFile(from);
|
|
49
47
|
if (oneFile) {
|
|
50
|
-
return this._copySingleAsync(
|
|
48
|
+
return this._copySingleAsync(
|
|
49
|
+
oneFile,
|
|
50
|
+
renderFilepath(to, context, tplSettings),
|
|
51
|
+
options
|
|
52
|
+
);
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
const fromGlob = util.globify(from);
|
|
54
56
|
|
|
55
|
-
const globOptions = {...options.globOptions, nodir: true};
|
|
56
|
-
const diskFiles = globby
|
|
57
|
+
const globOptions = { ...options.globOptions, nodir: true };
|
|
58
|
+
const diskFiles = globby
|
|
59
|
+
.sync(fromGlob, globOptions)
|
|
60
|
+
.map((filepath) => path.resolve(filepath));
|
|
57
61
|
const storeFiles = [];
|
|
58
|
-
this.store.each(file => {
|
|
62
|
+
this.store.each((file) => {
|
|
59
63
|
// The store may have a glob path and when we try to copy it will fail because not real file
|
|
60
|
-
if (
|
|
64
|
+
if (
|
|
65
|
+
!globby.hasMagic(normalize(file.path)) &&
|
|
66
|
+
multimatch([file.path], fromGlob).length !== 0 &&
|
|
67
|
+
!diskFiles.includes(file.path)
|
|
68
|
+
) {
|
|
61
69
|
storeFiles.push(file.path);
|
|
62
70
|
}
|
|
63
71
|
});
|
|
@@ -66,23 +74,40 @@ exports.copyAsync = async function (from, to, options, context, tplSettings) {
|
|
|
66
74
|
if (Array.isArray(from) || !this.exists(from) || globby.hasMagic(normalize(from))) {
|
|
67
75
|
assert(
|
|
68
76
|
!this.exists(to) || fs.statSync(to).isDirectory(),
|
|
69
|
-
'When copying multiple files, provide a directory as destination'
|
|
77
|
+
'When copying multiple files, provide a directory as destination'
|
|
70
78
|
);
|
|
71
79
|
|
|
72
|
-
const processDestinationPath = options.processDestinationPath || (path => path);
|
|
80
|
+
const processDestinationPath = options.processDestinationPath || ((path) => path);
|
|
73
81
|
const root = util.getCommonPath(from);
|
|
74
|
-
generateDestination = filepath => {
|
|
82
|
+
generateDestination = (filepath) => {
|
|
75
83
|
const toFile = path.relative(root, filepath);
|
|
76
84
|
return processDestinationPath(path.join(to, toFile));
|
|
77
85
|
};
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
// Sanity checks: Makes sure we copy at least one file.
|
|
81
|
-
assert(
|
|
89
|
+
assert(
|
|
90
|
+
options.ignoreNoMatch || diskFiles.length > 0 || storeFiles.length > 0,
|
|
91
|
+
'Trying to copy from a source that does not exist: ' + from
|
|
92
|
+
);
|
|
82
93
|
|
|
83
94
|
await Promise.all([
|
|
84
|
-
...diskFiles.map(
|
|
85
|
-
|
|
95
|
+
...diskFiles.map((file) =>
|
|
96
|
+
this._copySingleAsync(
|
|
97
|
+
file,
|
|
98
|
+
renderFilepath(generateDestination(file), context, tplSettings),
|
|
99
|
+
options
|
|
100
|
+
)
|
|
101
|
+
),
|
|
102
|
+
...storeFiles.map((file) =>
|
|
103
|
+
Promise.resolve(
|
|
104
|
+
this._copySingle(
|
|
105
|
+
file,
|
|
106
|
+
renderFilepath(generateDestination(file), context, tplSettings),
|
|
107
|
+
options
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
),
|
|
86
111
|
]);
|
|
87
112
|
};
|
|
88
113
|
|
|
@@ -99,7 +124,7 @@ exports._copySingleAsync = async function (from, to, options = {}) {
|
|
|
99
124
|
}
|
|
100
125
|
|
|
101
126
|
if (this.store.existsInMemory(to)) {
|
|
102
|
-
this.append(to, contents, {create: true, ...options});
|
|
127
|
+
this.append(to, contents, { create: true, ...options });
|
|
103
128
|
return;
|
|
104
129
|
}
|
|
105
130
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const ejs = require('ejs');
|
|
4
3
|
const fs = require('fs').promises;
|
|
5
|
-
const {isBinary} = require('../util');
|
|
4
|
+
const { isBinary, renderFile } = require('../util');
|
|
6
5
|
|
|
7
6
|
module.exports = async function (from, to, context, tplSettings, options) {
|
|
8
7
|
context = context || {};
|
|
@@ -12,18 +11,25 @@ module.exports = async function (from, to, context, tplSettings, options) {
|
|
|
12
11
|
from,
|
|
13
12
|
to,
|
|
14
13
|
{
|
|
15
|
-
processDestinationPath: path => path.replace(/.ejs$/, ''),
|
|
14
|
+
processDestinationPath: (path) => path.replace(/.ejs$/, ''),
|
|
16
15
|
...options,
|
|
17
16
|
async processFile(filename) {
|
|
18
17
|
if (isBinary(filename, null)) {
|
|
19
18
|
return fs.readFile(filename);
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
return
|
|
21
|
+
return renderFile(filename, context, tplSettings);
|
|
23
22
|
},
|
|
24
|
-
process: (contents, filename, destination) =>
|
|
23
|
+
process: (contents, filename, destination) =>
|
|
24
|
+
this._processTpl({
|
|
25
|
+
contents,
|
|
26
|
+
filename,
|
|
27
|
+
destination,
|
|
28
|
+
context,
|
|
29
|
+
tplSettings,
|
|
30
|
+
}),
|
|
25
31
|
},
|
|
26
32
|
context,
|
|
27
|
-
tplSettings
|
|
33
|
+
tplSettings
|
|
28
34
|
);
|
|
29
35
|
};
|
package/lib/actions/copy-tpl.js
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const {isBinary} = require('../util');
|
|
3
|
+
const { isBinary, render } = require('../util');
|
|
5
4
|
|
|
6
|
-
module.exports._processTpl = function ({contents, filename, context, tplSettings}) {
|
|
5
|
+
module.exports._processTpl = function ({ contents, filename, context, tplSettings }) {
|
|
7
6
|
if (isBinary(filename, contents)) {
|
|
8
7
|
return contents;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
return
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
...tplSettings,
|
|
18
|
-
},
|
|
19
|
-
);
|
|
10
|
+
return render(contents.toString(), context, {
|
|
11
|
+
// Setting filename by default allow including partials.
|
|
12
|
+
filename,
|
|
13
|
+
cache: true,
|
|
14
|
+
...tplSettings,
|
|
15
|
+
});
|
|
20
16
|
};
|
|
21
17
|
|
|
22
18
|
module.exports.copyTpl = function (from, to, context, tplSettings, options) {
|
|
@@ -27,11 +23,12 @@ module.exports.copyTpl = function (from, to, context, tplSettings, options) {
|
|
|
27
23
|
from,
|
|
28
24
|
to,
|
|
29
25
|
{
|
|
30
|
-
processDestinationPath: path => path.replace(/.ejs$/, ''),
|
|
26
|
+
processDestinationPath: (path) => path.replace(/.ejs$/, ''),
|
|
31
27
|
...options,
|
|
32
|
-
process: (contents, filename) =>
|
|
28
|
+
process: (contents, filename) =>
|
|
29
|
+
this._processTpl({ contents, filename, context, tplSettings }),
|
|
33
30
|
},
|
|
34
31
|
context,
|
|
35
|
-
tplSettings
|
|
32
|
+
tplSettings
|
|
36
33
|
);
|
|
37
34
|
};
|
package/lib/actions/copy.js
CHANGED
|
@@ -5,7 +5,6 @@ const fs = require('fs');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const globby = require('globby');
|
|
7
7
|
const multimatch = require('multimatch');
|
|
8
|
-
const ejs = require('ejs');
|
|
9
8
|
const util = require('../util');
|
|
10
9
|
const normalize = require('normalize-path');
|
|
11
10
|
|
|
@@ -22,43 +21,54 @@ exports.copy = function (from, to, options, context, tplSettings) {
|
|
|
22
21
|
let diskFiles = [];
|
|
23
22
|
if (options.noGlob) {
|
|
24
23
|
const fromFiles = Array.isArray(fromGlob) ? fromGlob : [fromGlob];
|
|
25
|
-
diskFiles = fromFiles.filter(filepath => fs.existsSync(filepath));
|
|
24
|
+
diskFiles = fromFiles.filter((filepath) => fs.existsSync(filepath));
|
|
26
25
|
} else {
|
|
27
|
-
const globOptions = {...options.globOptions, nodir: true};
|
|
28
|
-
diskFiles = globby.sync(fromGlob, globOptions).map(file => path.resolve(file));
|
|
26
|
+
const globOptions = { ...options.globOptions, nodir: true };
|
|
27
|
+
diskFiles = globby.sync(fromGlob, globOptions).map((file) => path.resolve(file));
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
const storeFiles = [];
|
|
32
|
-
this.store.each(file => {
|
|
31
|
+
this.store.each((file) => {
|
|
33
32
|
// The store may have a glob path and when we try to copy it will fail because not real file
|
|
34
|
-
if (
|
|
33
|
+
if (
|
|
34
|
+
!globby.hasMagic(normalize(file.path)) &&
|
|
35
|
+
multimatch([file.path], fromGlob).length !== 0 &&
|
|
36
|
+
!diskFiles.includes(file.path)
|
|
37
|
+
) {
|
|
35
38
|
storeFiles.push(file.path);
|
|
36
39
|
}
|
|
37
40
|
});
|
|
38
41
|
const files = diskFiles.concat(storeFiles);
|
|
39
42
|
|
|
40
43
|
let generateDestination = () => to;
|
|
41
|
-
if (
|
|
44
|
+
if (
|
|
45
|
+
Array.isArray(from) ||
|
|
46
|
+
!this.exists(from) ||
|
|
47
|
+
(globby.hasMagic(normalize(from)) && !options.noGlob)
|
|
48
|
+
) {
|
|
42
49
|
assert(
|
|
43
50
|
!this.exists(to) || fs.statSync(to).isDirectory(),
|
|
44
|
-
'When copying multiple files, provide a directory as destination'
|
|
51
|
+
'When copying multiple files, provide a directory as destination'
|
|
45
52
|
);
|
|
46
53
|
|
|
47
|
-
const processDestinationPath = options.processDestinationPath || (path => path);
|
|
54
|
+
const processDestinationPath = options.processDestinationPath || ((path) => path);
|
|
48
55
|
const root = util.getCommonPath(from);
|
|
49
|
-
generateDestination = filepath => {
|
|
56
|
+
generateDestination = (filepath) => {
|
|
50
57
|
const toFile = path.relative(root, filepath);
|
|
51
58
|
return processDestinationPath(path.join(to, toFile));
|
|
52
59
|
};
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
// Sanity checks: Makes sure we copy at least one file.
|
|
56
|
-
assert(
|
|
63
|
+
assert(
|
|
64
|
+
options.ignoreNoMatch || files.length > 0,
|
|
65
|
+
'Trying to copy from a source that does not exist: ' + from
|
|
66
|
+
);
|
|
57
67
|
|
|
58
|
-
files.forEach(file => {
|
|
68
|
+
files.forEach((file) => {
|
|
59
69
|
let toFile = generateDestination(file);
|
|
60
70
|
if (context) {
|
|
61
|
-
toFile =
|
|
71
|
+
toFile = util.render(toFile, context, { ...tplSettings, cache: false });
|
|
62
72
|
}
|
|
63
73
|
|
|
64
74
|
this._copySingle(file, toFile, options, context, tplSettings);
|
|
@@ -70,7 +80,7 @@ exports._copySingle = function (from, to, options = {}) {
|
|
|
70
80
|
|
|
71
81
|
const file = this.store.get(from);
|
|
72
82
|
|
|
73
|
-
let {contents} = file;
|
|
83
|
+
let { contents } = file;
|
|
74
84
|
if (options.process) {
|
|
75
85
|
contents = applyProcessingFunc(options.process, file.contents, file.path);
|
|
76
86
|
}
|
|
@@ -81,7 +91,7 @@ exports._copySingle = function (from, to, options = {}) {
|
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
if (this.store.existsInMemory(to)) {
|
|
84
|
-
this.append(to, contents, {create: true, ...options});
|
|
94
|
+
this.append(to, contents, { create: true, ...options });
|
|
85
95
|
return;
|
|
86
96
|
}
|
|
87
97
|
}
|
package/lib/actions/delete.js
CHANGED
|
@@ -4,7 +4,7 @@ const path = require('path');
|
|
|
4
4
|
const globby = require('globby');
|
|
5
5
|
const multimatch = require('multimatch');
|
|
6
6
|
const util = require('../util');
|
|
7
|
-
const {setDeletedFileState} = require('../state');
|
|
7
|
+
const { setDeletedFileState } = require('../state');
|
|
8
8
|
|
|
9
9
|
function deleteFile(path, store) {
|
|
10
10
|
const file = store.get(path);
|
|
@@ -18,17 +18,17 @@ module.exports = function (paths, options) {
|
|
|
18
18
|
paths = [paths];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
paths = paths.map(filePath => path.resolve(filePath));
|
|
21
|
+
paths = paths.map((filePath) => path.resolve(filePath));
|
|
22
22
|
paths = util.globify(paths);
|
|
23
23
|
options = options || {};
|
|
24
24
|
|
|
25
25
|
const globOptions = options.globOptions || {};
|
|
26
26
|
const files = globby.sync(paths, globOptions);
|
|
27
|
-
files.forEach(file => {
|
|
27
|
+
files.forEach((file) => {
|
|
28
28
|
deleteFile(file, this.store);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
this.store.each(file => {
|
|
31
|
+
this.store.each((file) => {
|
|
32
32
|
if (multimatch([file.path], paths).length !== 0) {
|
|
33
33
|
deleteFile(file.path, this.store);
|
|
34
34
|
}
|
package/lib/actions/dump.js
CHANGED
|
@@ -2,25 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const normalize = require('normalize-path');
|
|
5
|
+
const minimatch = require('minimatch');
|
|
5
6
|
|
|
6
|
-
const {hasClearedState, hasState, STATE, STATE_CLEARED} = require('../state');
|
|
7
|
+
const { hasClearedState, hasState, STATE, STATE_CLEARED } = require('../state');
|
|
8
|
+
|
|
9
|
+
module.exports = function (
|
|
10
|
+
cwd = process.cwd(),
|
|
11
|
+
filter = (file) => hasClearedState(file) || hasState(file)
|
|
12
|
+
) {
|
|
13
|
+
if (typeof filter === 'string') {
|
|
14
|
+
const pattern = filter;
|
|
15
|
+
filter = (file) => minimatch(file.path, pattern);
|
|
16
|
+
}
|
|
7
17
|
|
|
8
|
-
module.exports = function (cwd = process.cwd(), filter = file => hasClearedState(file) || hasState(file)) {
|
|
9
18
|
return Object.fromEntries(
|
|
10
|
-
this.store
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
this.store
|
|
20
|
+
.all()
|
|
21
|
+
.filter((file) => filter(file, cwd))
|
|
22
|
+
.map((file) => {
|
|
23
|
+
const filePath = normalize(cwd ? path.relative(cwd, file.path) : file.path);
|
|
24
|
+
const fileDump = {
|
|
25
|
+
contents: file.contents ? file.contents.toString() : file.contents,
|
|
26
|
+
};
|
|
27
|
+
if (file[STATE]) {
|
|
28
|
+
fileDump[STATE] = file[STATE];
|
|
29
|
+
}
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
if (file[STATE_CLEARED]) {
|
|
32
|
+
fileDump[STATE_CLEARED] = file[STATE_CLEARED];
|
|
33
|
+
}
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
return [filePath, fileDump];
|
|
36
|
+
})
|
|
25
37
|
);
|
|
26
38
|
};
|
package/lib/actions/read-json.js
CHANGED
|
@@ -5,7 +5,9 @@ module.exports = function (filepath, defaults) {
|
|
|
5
5
|
try {
|
|
6
6
|
return JSON.parse(this.read(filepath));
|
|
7
7
|
} catch (error) {
|
|
8
|
-
throw new Error(
|
|
8
|
+
throw new Error(
|
|
9
|
+
'Could not parse JSON in file: ' + filepath + '. Detail: ' + error.message
|
|
10
|
+
);
|
|
9
11
|
}
|
|
10
12
|
} else {
|
|
11
13
|
return defaults;
|
package/lib/actions/read.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
module.exports = function (filepath, options) {
|
|
4
|
-
options = options || {raw: false};
|
|
4
|
+
options = options || { raw: false };
|
|
5
5
|
const file = this.store.get(filepath);
|
|
6
6
|
|
|
7
7
|
if (file.contents === null) {
|
|
@@ -9,7 +9,7 @@ module.exports = function (filepath, options) {
|
|
|
9
9
|
return options.defaults;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
throw new Error(filepath +
|
|
12
|
+
throw new Error(filepath + " doesn't exist");
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
return options.raw ? file.contents : file.contents.toString();
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
const DEFAULT_INDENTATION = 2;
|
|
4
4
|
|
|
5
5
|
module.exports = function (filepath, contents, replacer, space) {
|
|
6
|
-
const jsonStr =
|
|
6
|
+
const jsonStr =
|
|
7
|
+
JSON.stringify(contents, replacer || null, space || DEFAULT_INDENTATION) + '\n';
|
|
7
8
|
|
|
8
9
|
return this.write(filepath, jsonStr);
|
|
9
10
|
};
|
package/lib/actions/write.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const assert = require('assert');
|
|
4
|
-
const {isFileStateModified, setModifiedFileState} = require('../state');
|
|
4
|
+
const { isFileStateModified, setModifiedFileState } = require('../state');
|
|
5
5
|
|
|
6
6
|
module.exports = function (filepath, contents, stat) {
|
|
7
7
|
assert(
|
|
8
8
|
typeof contents === 'string' || Buffer.isBuffer(contents),
|
|
9
|
-
'Expected `contents` to be a String or a Buffer'
|
|
9
|
+
'Expected `contents` to be a String or a Buffer'
|
|
10
10
|
);
|
|
11
11
|
|
|
12
12
|
const file = this.store.get(filepath);
|
|
13
13
|
const newContents = Buffer.isBuffer(contents) ? contents : Buffer.from(contents);
|
|
14
14
|
if (
|
|
15
|
-
!isFileStateModified(file)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
!isFileStateModified(file) ||
|
|
16
|
+
!Buffer.isBuffer(file.contents) ||
|
|
17
|
+
!newContents.equals(file.contents) ||
|
|
18
|
+
(stat !== undefined && file.stat !== stat)
|
|
19
19
|
) {
|
|
20
20
|
setModifiedFileState(file);
|
|
21
21
|
file.contents = newContents;
|
package/lib/index.js
CHANGED
|
@@ -18,7 +18,8 @@ EditionInterface.prototype._copySingle = require('./actions/copy.js')._copySingl
|
|
|
18
18
|
EditionInterface.prototype.copyTpl = require('./actions/copy-tpl.js').copyTpl;
|
|
19
19
|
EditionInterface.prototype._processTpl = require('./actions/copy-tpl.js')._processTpl;
|
|
20
20
|
EditionInterface.prototype.copyAsync = require('./actions/copy-async.js').copyAsync;
|
|
21
|
-
EditionInterface.prototype._copySingleAsync =
|
|
21
|
+
EditionInterface.prototype._copySingleAsync =
|
|
22
|
+
require('./actions/copy-async.js')._copySingleAsync;
|
|
22
23
|
EditionInterface.prototype.copyTplAsync = require('./actions/copy-tpl-async.js');
|
|
23
24
|
EditionInterface.prototype.move = require('./actions/move.js');
|
|
24
25
|
EditionInterface.prototype.commit = require('./actions/commit.js');
|
package/lib/state.js
CHANGED
|
@@ -14,7 +14,7 @@ const setFileState = (file, state) => {
|
|
|
14
14
|
file[STATE] = state;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
const isFileNew = file => {
|
|
17
|
+
const isFileNew = (file) => {
|
|
18
18
|
if (file[IS_NEW] === undefined) {
|
|
19
19
|
file[IS_NEW] = !fs.existsSync(file.path);
|
|
20
20
|
}
|
|
@@ -22,27 +22,28 @@ const isFileNew = file => {
|
|
|
22
22
|
return file[IS_NEW];
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
const isFileStateModified = file => file[STATE] === STATE_MODIFIED;
|
|
25
|
+
const isFileStateModified = (file) => file[STATE] === STATE_MODIFIED;
|
|
26
26
|
|
|
27
|
-
const setModifiedFileState = file => setFileState(file, STATE_MODIFIED);
|
|
27
|
+
const setModifiedFileState = (file) => setFileState(file, STATE_MODIFIED);
|
|
28
28
|
|
|
29
|
-
const isFileStateDeleted = file => file[STATE] === STATE_DELETED;
|
|
29
|
+
const isFileStateDeleted = (file) => file[STATE] === STATE_DELETED;
|
|
30
30
|
|
|
31
|
-
const setDeletedFileState = file => setFileState(file, STATE_DELETED);
|
|
31
|
+
const setDeletedFileState = (file) => setFileState(file, STATE_DELETED);
|
|
32
32
|
|
|
33
|
-
const isFilePending =
|
|
33
|
+
const isFilePending = (file) =>
|
|
34
|
+
isFileStateModified(file) || (isFileStateDeleted(file) && !isFileNew(file));
|
|
34
35
|
|
|
35
|
-
const setCommittedFile = file => {
|
|
36
|
+
const setCommittedFile = (file) => {
|
|
36
37
|
file.committed = true;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
|
-
const isFileCommitted = file => Boolean(file.committed);
|
|
40
|
+
const isFileCommitted = (file) => Boolean(file.committed);
|
|
40
41
|
|
|
41
|
-
const resetFileState = file => {
|
|
42
|
+
const resetFileState = (file) => {
|
|
42
43
|
delete file[STATE];
|
|
43
44
|
};
|
|
44
45
|
|
|
45
|
-
const clearFileState = file => {
|
|
46
|
+
const clearFileState = (file) => {
|
|
46
47
|
if (file[STATE]) {
|
|
47
48
|
file[STATE_CLEARED] = file[STATE];
|
|
48
49
|
}
|
|
@@ -51,9 +52,9 @@ const clearFileState = file => {
|
|
|
51
52
|
delete file[IS_NEW];
|
|
52
53
|
};
|
|
53
54
|
|
|
54
|
-
const hasState = file => Boolean(file[STATE]);
|
|
55
|
+
const hasState = (file) => Boolean(file[STATE]);
|
|
55
56
|
|
|
56
|
-
const hasClearedState = file => Boolean(file[STATE_CLEARED]);
|
|
57
|
+
const hasClearedState = (file) => Boolean(file[STATE_CLEARED]);
|
|
57
58
|
|
|
58
59
|
module.exports = {
|
|
59
60
|
STATE,
|
package/lib/transform.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
const {createTransform} = require('./util');
|
|
2
|
-
const {isFilePending} = require('./state');
|
|
1
|
+
const { createTransform } = require('./util');
|
|
2
|
+
const { isFilePending } = require('./state');
|
|
3
3
|
|
|
4
|
-
const createPendingFilesPassthrough = () =>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const createPendingFilesPassthrough = () =>
|
|
5
|
+
createTransform((file, _enc, cb) => {
|
|
6
|
+
// Don't process deleted file who haven't been commited yet.
|
|
7
|
+
cb(undefined, isFilePending(file) ? file : undefined);
|
|
8
|
+
});
|
|
8
9
|
|
|
9
|
-
const createCommitTransform = memFsEditor
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const createCommitTransform = (memFsEditor) =>
|
|
11
|
+
createTransform((file, _enc, cb) => {
|
|
12
|
+
memFsEditor
|
|
13
|
+
.commitFileAsync(file)
|
|
14
|
+
.then(() => cb())
|
|
15
|
+
.catch((error) => cb(error));
|
|
16
|
+
});
|
|
12
17
|
|
|
13
|
-
module.exports = {createPendingFilesPassthrough, createCommitTransform};
|
|
18
|
+
module.exports = { createPendingFilesPassthrough, createCommitTransform };
|
package/lib/util.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const ejs = require('ejs');
|
|
3
4
|
const fs = require('fs');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
const commondir = require('commondir');
|
|
6
7
|
const globby = require('globby');
|
|
7
8
|
const normalize = require('normalize-path');
|
|
8
|
-
const {Transform} = require('stream');
|
|
9
|
-
const {isBinaryFileSync} = require('isbinaryfile');
|
|
9
|
+
const { Transform } = require('stream');
|
|
10
|
+
const { isBinaryFileSync } = require('isbinaryfile');
|
|
10
11
|
|
|
11
|
-
const {default: textextensions} = require('textextensions');
|
|
12
|
-
const {default: binaryextensions} = require('binaryextensions');
|
|
12
|
+
const { default: textextensions } = require('textextensions');
|
|
13
|
+
const { default: binaryextensions } = require('binaryextensions');
|
|
13
14
|
|
|
14
15
|
function notNullOrExclusion(file) {
|
|
15
16
|
return file != null && file.charAt(0) !== '!';
|
|
@@ -17,9 +18,7 @@ function notNullOrExclusion(file) {
|
|
|
17
18
|
|
|
18
19
|
exports.getCommonPath = function (filePath) {
|
|
19
20
|
if (Array.isArray(filePath)) {
|
|
20
|
-
filePath = filePath
|
|
21
|
-
.filter(notNullOrExclusion)
|
|
22
|
-
.map(this.getCommonPath.bind(this));
|
|
21
|
+
filePath = filePath.filter(notNullOrExclusion).map(this.getCommonPath.bind(this));
|
|
23
22
|
|
|
24
23
|
return commondir(filePath);
|
|
25
24
|
}
|
|
@@ -36,7 +35,10 @@ exports.getCommonPath = function (filePath) {
|
|
|
36
35
|
|
|
37
36
|
exports.globify = function (filePath) {
|
|
38
37
|
if (Array.isArray(filePath)) {
|
|
39
|
-
return filePath.reduce(
|
|
38
|
+
return filePath.reduce(
|
|
39
|
+
(memo, pattern) => memo.concat(this.globify(normalize(pattern))),
|
|
40
|
+
[]
|
|
41
|
+
);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
filePath = normalize(filePath);
|
|
@@ -48,10 +50,7 @@ exports.globify = function (filePath) {
|
|
|
48
50
|
if (!fs.existsSync(filePath)) {
|
|
49
51
|
// The target of a pattern who's not a glob and doesn't match an existing
|
|
50
52
|
// entity on the disk is ambiguous. As such, match both files and directories.
|
|
51
|
-
return [
|
|
52
|
-
filePath,
|
|
53
|
-
normalize(path.join(filePath, '**')),
|
|
54
|
-
];
|
|
53
|
+
return [filePath, normalize(path.join(filePath, '**'))];
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
const fsStats = fs.statSync(filePath);
|
|
@@ -67,13 +66,12 @@ exports.globify = function (filePath) {
|
|
|
67
66
|
};
|
|
68
67
|
|
|
69
68
|
exports.createTransform = function (transform) {
|
|
70
|
-
|
|
69
|
+
return new Transform({
|
|
71
70
|
objectMode: true,
|
|
72
71
|
transform(...args) {
|
|
73
72
|
return transform.apply(this, args);
|
|
74
73
|
},
|
|
75
74
|
});
|
|
76
|
-
return stream;
|
|
77
75
|
};
|
|
78
76
|
|
|
79
77
|
exports.isBinary = (filePath, newFileContents) => {
|
|
@@ -87,7 +85,18 @@ exports.isBinary = (filePath, newFileContents) => {
|
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
return (
|
|
90
|
-
(fs.existsSync(filePath) && isBinaryFileSync(filePath))
|
|
91
|
-
|
|
88
|
+
(fs.existsSync(filePath) && isBinaryFileSync(filePath)) ||
|
|
89
|
+
(newFileContents &&
|
|
90
|
+
isBinaryFileSync(
|
|
91
|
+
Buffer.isBuffer(newFileContents) ? newFileContents : Buffer.from(newFileContents)
|
|
92
|
+
))
|
|
92
93
|
);
|
|
93
94
|
};
|
|
95
|
+
|
|
96
|
+
exports.render = function (template, data, options) {
|
|
97
|
+
return ejs.render(template, data, { cache: false, ...options });
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
exports.renderFile = function (template, data, options) {
|
|
101
|
+
return ejs.renderFile(template, data, { cache: true, ...options });
|
|
102
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mem-fs-editor",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.5.0",
|
|
4
4
|
"description": "File edition helpers working on top of mem-fs",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"fix": "eslint . --fix",
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
"binaryextensions": "^4.16.0",
|
|
19
19
|
"commondir": "^1.0.1",
|
|
20
20
|
"deep-extend": "^0.6.0",
|
|
21
|
-
"ejs": "^3.1.
|
|
22
|
-
"globby": "^11.0
|
|
21
|
+
"ejs": "^3.1.8",
|
|
22
|
+
"globby": "^11.1.0",
|
|
23
23
|
"isbinaryfile": "^4.0.8",
|
|
24
|
+
"minimatch": "^3.1.2",
|
|
24
25
|
"multimatch": "^5.0.0",
|
|
25
26
|
"normalize-path": "^3.0.0",
|
|
26
27
|
"textextensions": "^5.13.0"
|
|
@@ -36,11 +37,14 @@
|
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"coveralls": "^3.0.3",
|
|
38
39
|
"escape-regexp": "0.0.1",
|
|
39
|
-
"eslint": "^
|
|
40
|
-
"eslint-config-
|
|
40
|
+
"eslint": "^8.5.0",
|
|
41
|
+
"eslint-config-prettier": "^8.3.0",
|
|
42
|
+
"eslint-config-xo": "^0.39.0",
|
|
43
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
41
44
|
"jest": "^27.0.6",
|
|
42
45
|
"mem-fs": "^2.2.1",
|
|
43
|
-
"
|
|
46
|
+
"prettier": "^2.5.1",
|
|
47
|
+
"sinon": "^12.0.1"
|
|
44
48
|
},
|
|
45
49
|
"jest": {
|
|
46
50
|
"collectCoverage": true,
|