@w5s/dev 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/block.d.ts +45 -0
- package/dist/block.d.ts.map +1 -0
- package/dist/block.js +88 -0
- package/dist/block.js.map +1 -0
- package/dist/file.d.ts +52 -0
- package/dist/file.d.ts.map +1 -0
- package/dist/file.js +82 -0
- package/dist/file.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/json.d.ts +37 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +34 -0
- package/dist/json.js.map +1 -0
- package/dist/project.d.ts +28 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +24 -11
- package/dist/project.js.map +1 -1
- package/package.json +2 -2
- package/src/block.ts +124 -0
- package/src/file.ts +97 -0
- package/src/index.ts +3 -0
- package/src/json.ts +57 -0
- package/src/project.ts +45 -11
package/dist/block.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface BlockOptions {
|
|
2
|
+
/**
|
|
3
|
+
* The marker builder function that will take either `markerBegin` or `markerEnd`
|
|
4
|
+
*
|
|
5
|
+
* @default '# ${mark} MANAGED BLOCK'
|
|
6
|
+
*/
|
|
7
|
+
marker?: (mark: 'Begin' | 'End') => string;
|
|
8
|
+
/**
|
|
9
|
+
* File path
|
|
10
|
+
*/
|
|
11
|
+
path: string;
|
|
12
|
+
/**
|
|
13
|
+
* Block content to insert
|
|
14
|
+
*/
|
|
15
|
+
block: string;
|
|
16
|
+
/**
|
|
17
|
+
* Insert position
|
|
18
|
+
*/
|
|
19
|
+
insertPosition?: ['before', 'BeginningOfFile'] | ['after', 'EndOfFile'];
|
|
20
|
+
/**
|
|
21
|
+
* Block target state
|
|
22
|
+
*/
|
|
23
|
+
state?: 'present' | 'absent';
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Replace asynchronously a block in file that follows pattern :
|
|
27
|
+
*
|
|
28
|
+
* marker(markerBegin)
|
|
29
|
+
* ...
|
|
30
|
+
* marker(markerEnd)
|
|
31
|
+
*
|
|
32
|
+
* @param options
|
|
33
|
+
*/
|
|
34
|
+
export declare function block(options: BlockOptions): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Replace synchronously a block in file that follows pattern :
|
|
37
|
+
*
|
|
38
|
+
* marker(markerBegin)
|
|
39
|
+
* ...
|
|
40
|
+
* marker(markerEnd)
|
|
41
|
+
*
|
|
42
|
+
* @param options
|
|
43
|
+
*/
|
|
44
|
+
export declare function blockSync(options: BlockOptions): void;
|
|
45
|
+
//# sourceMappingURL=block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block.d.ts","sourceRoot":"","sources":["../src/block.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,KAAK,KAAK,MAAM,CAAC;IAC3C;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACxE;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CAC9B;AA0ED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,iBAE1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,QAE9C"}
|
package/dist/block.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.blockSync = exports.block = void 0;
|
|
4
|
+
const file_js_1 = require("./file.js");
|
|
5
|
+
const EOF = 'EndOfFile';
|
|
6
|
+
const BOF = 'BeginningOfFile';
|
|
7
|
+
function toFileOptions(options) {
|
|
8
|
+
const { marker = (mark) => `# ${mark.toUpperCase()} MANAGED BLOCK`, path, block: blockName, insertPosition = ['after', EOF], state = 'present', } = options;
|
|
9
|
+
const EOL = '\n';
|
|
10
|
+
const beginBlock = marker('Begin');
|
|
11
|
+
const endBlock = marker('End');
|
|
12
|
+
/**
|
|
13
|
+
* @param content
|
|
14
|
+
*/
|
|
15
|
+
function findBlock(content) {
|
|
16
|
+
const startIndex = content.indexOf(beginBlock);
|
|
17
|
+
const endIndex = content.indexOf(endBlock) + endBlock.length;
|
|
18
|
+
return {
|
|
19
|
+
endIndex,
|
|
20
|
+
exists: startIndex >= 0 && endIndex >= 0,
|
|
21
|
+
startIndex,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function apply(fullContent, blockContent) {
|
|
25
|
+
const found = findBlock(fullContent);
|
|
26
|
+
const remove = state === 'absent';
|
|
27
|
+
const replaceBlock = remove ? '' : beginBlock + EOL + blockContent + EOL + endBlock;
|
|
28
|
+
const [positionDirection, positionAnchor] = insertPosition;
|
|
29
|
+
if (found.exists) {
|
|
30
|
+
return fullContent.slice(0, found.startIndex) + replaceBlock + fullContent.slice(found.endIndex);
|
|
31
|
+
}
|
|
32
|
+
if (remove) {
|
|
33
|
+
return fullContent;
|
|
34
|
+
}
|
|
35
|
+
switch (positionDirection) {
|
|
36
|
+
case 'after': {
|
|
37
|
+
// insert
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
39
|
+
if (positionAnchor === EOF) {
|
|
40
|
+
return fullContent + EOL + replaceBlock;
|
|
41
|
+
}
|
|
42
|
+
return fullContent;
|
|
43
|
+
}
|
|
44
|
+
case 'before': {
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
46
|
+
if (positionAnchor === BOF) {
|
|
47
|
+
return replaceBlock + EOL + fullContent;
|
|
48
|
+
}
|
|
49
|
+
return fullContent;
|
|
50
|
+
}
|
|
51
|
+
default: {
|
|
52
|
+
throw new Error(`Unsupported position ${String(positionDirection)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
path,
|
|
58
|
+
state: 'present',
|
|
59
|
+
update: (sourceContent) => apply(sourceContent, blockName),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Replace asynchronously a block in file that follows pattern :
|
|
64
|
+
*
|
|
65
|
+
* marker(markerBegin)
|
|
66
|
+
* ...
|
|
67
|
+
* marker(markerEnd)
|
|
68
|
+
*
|
|
69
|
+
* @param options
|
|
70
|
+
*/
|
|
71
|
+
function block(options) {
|
|
72
|
+
return (0, file_js_1.file)(toFileOptions(options));
|
|
73
|
+
}
|
|
74
|
+
exports.block = block;
|
|
75
|
+
/**
|
|
76
|
+
* Replace synchronously a block in file that follows pattern :
|
|
77
|
+
*
|
|
78
|
+
* marker(markerBegin)
|
|
79
|
+
* ...
|
|
80
|
+
* marker(markerEnd)
|
|
81
|
+
*
|
|
82
|
+
* @param options
|
|
83
|
+
*/
|
|
84
|
+
function blockSync(options) {
|
|
85
|
+
return (0, file_js_1.fileSync)(toFileOptions(options));
|
|
86
|
+
}
|
|
87
|
+
exports.blockSync = blockSync;
|
|
88
|
+
//# sourceMappingURL=block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block.js","sourceRoot":"","sources":["../src/block.ts"],"names":[],"mappings":"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"}
|
package/dist/file.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export interface FileOptions {
|
|
3
|
+
/**
|
|
4
|
+
* File path
|
|
5
|
+
*/
|
|
6
|
+
readonly path: string;
|
|
7
|
+
/**
|
|
8
|
+
* File target state
|
|
9
|
+
*/
|
|
10
|
+
readonly state: 'present' | 'absent';
|
|
11
|
+
/**
|
|
12
|
+
* File content mapping function
|
|
13
|
+
*
|
|
14
|
+
* @param content
|
|
15
|
+
*/
|
|
16
|
+
readonly update?: (content: string) => string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* File encoding
|
|
19
|
+
*/
|
|
20
|
+
readonly encoding?: BufferEncoding;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Ensure file is present/absent with content initialized or modified with `update
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* await file({
|
|
28
|
+
* path: 'foo/bar',
|
|
29
|
+
* state: 'present',
|
|
30
|
+
* update: (content) => content + '_test', // This will append '_test' after current content
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @param options
|
|
35
|
+
*/
|
|
36
|
+
export declare function file(options: FileOptions): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Ensure file is present/absent with content initialized or modified with `update
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* fileSync({
|
|
43
|
+
* path: 'foo/bar',
|
|
44
|
+
* state: 'present',
|
|
45
|
+
* update: (content) => content + '_test', // This will append '_test' after current content
|
|
46
|
+
* })
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @param options
|
|
50
|
+
*/
|
|
51
|
+
export declare function fileSync(options: FileOptions): void;
|
|
52
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../src/file.ts"],"names":[],"mappings":";AAqBA,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1D;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;CACpC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAYnD"}
|
package/dist/file.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fileSync = exports.file = void 0;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
async function exists(path) {
|
|
7
|
+
try {
|
|
8
|
+
await (0, promises_1.access)(path, node_fs_1.constants.F_OK);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function existsSync(path) {
|
|
16
|
+
try {
|
|
17
|
+
(0, node_fs_1.accessSync)(path, node_fs_1.constants.F_OK);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Ensure file is present/absent with content initialized or modified with `update
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* await file({
|
|
30
|
+
* path: 'foo/bar',
|
|
31
|
+
* state: 'present',
|
|
32
|
+
* update: (content) => content + '_test', // This will append '_test' after current content
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @param options
|
|
37
|
+
*/
|
|
38
|
+
async function file(options) {
|
|
39
|
+
const { path, state, update, encoding = 'utf8' } = options;
|
|
40
|
+
if (state === 'present') {
|
|
41
|
+
const isPresent = await exists(path);
|
|
42
|
+
const previousContent = isPresent ? await (0, promises_1.readFile)(path, encoding) : '';
|
|
43
|
+
const newContent = update == null ? '' : update(previousContent);
|
|
44
|
+
if (newContent != null) {
|
|
45
|
+
await (0, promises_1.writeFile)(path, newContent, encoding);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
await (0, promises_1.rm)(path);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.file = file;
|
|
53
|
+
/**
|
|
54
|
+
* Ensure file is present/absent with content initialized or modified with `update
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* fileSync({
|
|
59
|
+
* path: 'foo/bar',
|
|
60
|
+
* state: 'present',
|
|
61
|
+
* update: (content) => content + '_test', // This will append '_test' after current content
|
|
62
|
+
* })
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @param options
|
|
66
|
+
*/
|
|
67
|
+
function fileSync(options) {
|
|
68
|
+
const { path, state, update, encoding = 'utf8' } = options;
|
|
69
|
+
if (state === 'present') {
|
|
70
|
+
const isPresent = existsSync(path);
|
|
71
|
+
const previousContent = isPresent ? (0, node_fs_1.readFileSync)(path, encoding) : '';
|
|
72
|
+
const newContent = update == null ? '' : update(previousContent);
|
|
73
|
+
if (newContent != null) {
|
|
74
|
+
(0, node_fs_1.writeFileSync)(path, newContent, encoding);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
(0, node_fs_1.rmSync)(path);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.fileSync = fileSync;
|
|
82
|
+
//# sourceMappingURL=file.js.map
|
package/dist/file.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../src/file.ts"],"names":[],"mappings":";;;AAAA,+CAAmE;AACnE,qCAAqF;AAErF,KAAK,UAAU,MAAM,CAAC,IAAY;IAChC,IAAI;QACF,MAAM,IAAA,iBAAM,EAAC,IAAI,EAAE,mBAAS,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;KACb;IAAC,MAAM;QACN,OAAO,KAAK,CAAC;KACd;AACH,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI;QACF,IAAA,oBAAU,EAAC,IAAI,EAAE,mBAAS,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;KACb;IAAC,MAAM;QACN,OAAO,KAAK,CAAC;KACd;AACH,CAAC;AAuBD;;;;;;;;;;;;;GAaG;AACI,KAAK,UAAU,IAAI,CAAC,OAAoB;IAC7C,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3D,IAAI,KAAK,KAAK,SAAS,EAAE;QACvB,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,IAAA,mBAAQ,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,MAAM,UAAU,GAAG,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACjE,IAAI,UAAU,IAAI,IAAI,EAAE;YACtB,MAAM,IAAA,oBAAS,EAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;SAC7C;KACF;SAAM;QACL,MAAM,IAAA,aAAE,EAAC,IAAI,CAAC,CAAC;KAChB;AACH,CAAC;AAZD,oBAYC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,QAAQ,CAAC,OAAoB;IAC3C,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3D,IAAI,KAAK,KAAK,SAAS,EAAE;QACvB,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,IAAA,sBAAY,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,MAAM,UAAU,GAAG,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACjE,IAAI,UAAU,IAAI,IAAI,EAAE;YACtB,IAAA,uBAAa,EAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;SAC3C;KACF;SAAM;QACL,IAAA,gBAAM,EAAC,IAAI,CAAC,CAAC;KACd;AACH,CAAC;AAZD,4BAYC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -15,5 +15,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./eslint.js"), exports);
|
|
18
|
+
__exportStar(require("./block.js"), exports);
|
|
19
|
+
__exportStar(require("./file.js"), exports);
|
|
20
|
+
__exportStar(require("./json.js"), exports);
|
|
18
21
|
__exportStar(require("./project.js"), exports);
|
|
19
22
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,+CAA6B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,6CAA2B;AAC3B,4CAA0B;AAC1B,4CAA0B;AAC1B,+CAA6B"}
|
package/dist/json.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export type JSONValue = null | number | string | boolean | JSONValue[] | {
|
|
3
|
+
[key: string]: JSONValue;
|
|
4
|
+
};
|
|
5
|
+
export interface JSONOption<V = JSONValue> {
|
|
6
|
+
/**
|
|
7
|
+
* File path
|
|
8
|
+
*/
|
|
9
|
+
readonly path: string;
|
|
10
|
+
/**
|
|
11
|
+
* File target state
|
|
12
|
+
*/
|
|
13
|
+
readonly state: 'present' | 'absent';
|
|
14
|
+
/**
|
|
15
|
+
* File content mapping function
|
|
16
|
+
*
|
|
17
|
+
* @param content
|
|
18
|
+
*/
|
|
19
|
+
readonly update?: (content: V | undefined) => V | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* File encoding
|
|
22
|
+
*/
|
|
23
|
+
readonly encoding?: BufferEncoding;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Ensure file is present/absent asynchronously with content value initialized or modified with `update`
|
|
27
|
+
*
|
|
28
|
+
* @param options
|
|
29
|
+
*/
|
|
30
|
+
export declare function json<Value>(options: JSONOption<Value>): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Ensure file is present/absent synchronously with content value initialized or modified with `update`
|
|
33
|
+
*
|
|
34
|
+
* @param options
|
|
35
|
+
*/
|
|
36
|
+
export declare function jsonSync<Value>(options: JSONOption<Value>): void;
|
|
37
|
+
//# sourceMappingURL=json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":";AAEA,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtG,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,SAAS;IACvC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,SAAS,KAAK,CAAC,GAAG,SAAS,CAAC;IAC5D;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;CACpC;AAiBD;;;;GAIG;AACH,wBAAsB,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3E;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAEhE"}
|
package/dist/json.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jsonSync = exports.json = void 0;
|
|
4
|
+
const file_js_1 = require("./file.js");
|
|
5
|
+
function toFileOption({ update, ...otherOptions }) {
|
|
6
|
+
return {
|
|
7
|
+
...otherOptions,
|
|
8
|
+
update: update == null
|
|
9
|
+
? update
|
|
10
|
+
: (content) => {
|
|
11
|
+
const jsonValue = content === '' ? undefined : JSON.parse(content);
|
|
12
|
+
return JSON.stringify(update(jsonValue));
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Ensure file is present/absent asynchronously with content value initialized or modified with `update`
|
|
18
|
+
*
|
|
19
|
+
* @param options
|
|
20
|
+
*/
|
|
21
|
+
async function json(options) {
|
|
22
|
+
return (0, file_js_1.file)(toFileOption(options));
|
|
23
|
+
}
|
|
24
|
+
exports.json = json;
|
|
25
|
+
/**
|
|
26
|
+
* Ensure file is present/absent synchronously with content value initialized or modified with `update`
|
|
27
|
+
*
|
|
28
|
+
* @param options
|
|
29
|
+
*/
|
|
30
|
+
function jsonSync(options) {
|
|
31
|
+
return (0, file_js_1.fileSync)(toFileOption(options));
|
|
32
|
+
}
|
|
33
|
+
exports.jsonSync = jsonSync;
|
|
34
|
+
//# sourceMappingURL=json.js.map
|
package/dist/json.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":";;;AAAA,uCAA6D;AAyB7D,SAAS,YAAY,CAAQ,EAAE,MAAM,EAAE,GAAG,YAAY,EAAqB;IACzE,OAAO;QACL,GAAG,YAAY;QAEf,MAAM,EACJ,MAAM,IAAI,IAAI;YACZ,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE;gBACV,MAAM,SAAS,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAW,CAAC;gBAE9E,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;YAC3C,CAAC;KACR,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,IAAI,CAAQ,OAA0B;IAC1D,OAAO,IAAA,cAAI,EAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AACrC,CAAC;AAFD,oBAEC;AAED;;;;GAIG;AACH,SAAgB,QAAQ,CAAQ,OAA0B;IACxD,OAAO,IAAA,kBAAQ,EAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;AACzC,CAAC;AAFD,4BAEC"}
|
package/dist/project.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
export declare namespace Project {
|
|
2
|
+
/**
|
|
3
|
+
* A type of a file extension
|
|
4
|
+
*/
|
|
2
5
|
type Extension = `.${string}`;
|
|
6
|
+
/**
|
|
7
|
+
* Object hash of all well-known file extension category to file extensions mapping
|
|
8
|
+
*/
|
|
9
|
+
interface ExtensionRegistry {
|
|
10
|
+
javascript: readonly Extension[];
|
|
11
|
+
javascriptreact: readonly Extension[];
|
|
12
|
+
typescript: readonly Extension[];
|
|
13
|
+
typescriptreact: readonly Extension[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A list of "vscode-like" language identifiers (i.e. "javascript", "javascriptreact")
|
|
17
|
+
*/
|
|
18
|
+
type LanguageId = keyof ExtensionRegistry;
|
|
3
19
|
/**
|
|
4
20
|
* Supported ECMA version
|
|
5
21
|
*
|
|
@@ -9,6 +25,18 @@ export declare namespace Project {
|
|
|
9
25
|
* ```
|
|
10
26
|
*/
|
|
11
27
|
function ecmaVersion(): 2022;
|
|
28
|
+
/**
|
|
29
|
+
* Return a list of extensions
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* Project.queryExtensions(['javascript']); // ['.js', '.cjs', ...]
|
|
34
|
+
* Project.queryExtensions(['typescript', 'typescriptreact']); // ['.ts', '.mts', ..., '.tsx']
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @param languages
|
|
38
|
+
*/
|
|
39
|
+
function queryExtensions(languages: LanguageId[]): readonly Extension[];
|
|
12
40
|
/**
|
|
13
41
|
* Supported file extensions
|
|
14
42
|
*
|
package/dist/project.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAIA,yBAAiB,OAAO,CAAC;IACvB,KAAY,SAAS,GAAG,IAAI,MAAM,EAAE,CAAC;IAErC;;;;;;;OAOG;IACH,SAAgB,WAAW,SAE1B;
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAIA,yBAAiB,OAAO,CAAC;IACvB;;OAEG;IACH,KAAY,SAAS,GAAG,IAAI,MAAM,EAAE,CAAC;IAErC;;OAEG;IACH,UAAiB,iBAAiB;QAChC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;QACjC,eAAe,EAAE,SAAS,SAAS,EAAE,CAAC;QACtC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;QACjC,eAAe,EAAE,SAAS,SAAS,EAAE,CAAC;KACvC;IAED;;OAEG;IACH,KAAY,UAAU,GAAG,MAAM,iBAAiB,CAAC;IAEjD;;;;;;;OAOG;IACH,SAAgB,WAAW,SAE1B;IASD;;;;;;;;;;OAUG;IACH,SAAgB,eAAe,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,SAAS,SAAS,EAAE,CAO7E;IAED;;;;;;;OAOG;IACH,SAAgB,gBAAgB,4BAE/B;IAkBD;;;;;;;OAOG;IACH,SAAgB,kBAAkB,4BAEjC;IAeD;;;;;;;OAOG;IACH,SAAgB,OAAO,sBAEtB;IAED;;;;;;;OAOG;IACH,SAAgB,mBAAmB,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAE5E;IAED;;;;;;;OAOG;IACH,SAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAEzE;CACF"}
|
package/dist/project.js
CHANGED
|
@@ -18,16 +18,29 @@ var Project;
|
|
|
18
18
|
return 2022;
|
|
19
19
|
}
|
|
20
20
|
Project.ecmaVersion = ecmaVersion;
|
|
21
|
-
const
|
|
22
|
-
'.
|
|
23
|
-
'.
|
|
24
|
-
'.cts',
|
|
25
|
-
'.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
const registry = {
|
|
22
|
+
javascript: ['.js', '.cjs', '.mjs'],
|
|
23
|
+
javascriptreact: ['.jsx'],
|
|
24
|
+
typescript: ['.ts', '.cts', '.mts'],
|
|
25
|
+
typescriptreact: ['.tsx'],
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Return a list of extensions
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* Project.queryExtensions(['javascript']); // ['.js', '.cjs', ...]
|
|
33
|
+
* Project.queryExtensions(['typescript', 'typescriptreact']); // ['.ts', '.mts', ..., '.tsx']
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @param languages
|
|
37
|
+
*/
|
|
38
|
+
function queryExtensions(languages) {
|
|
39
|
+
return languages
|
|
40
|
+
.reduce((previousValue, currentValue) => previousValue.concat(registry[currentValue] ?? []), [])
|
|
41
|
+
.sort();
|
|
42
|
+
}
|
|
43
|
+
Project.queryExtensions = queryExtensions;
|
|
31
44
|
/**
|
|
32
45
|
* Supported file extensions
|
|
33
46
|
*
|
|
@@ -37,7 +50,7 @@ var Project;
|
|
|
37
50
|
* ```
|
|
38
51
|
*/
|
|
39
52
|
function sourceExtensions() {
|
|
40
|
-
return
|
|
53
|
+
return queryExtensions(['javascript', 'javascriptreact', 'typescript', 'typescriptreact']);
|
|
41
54
|
}
|
|
42
55
|
Project.sourceExtensions = sourceExtensions;
|
|
43
56
|
const RESOURCE_EXTENSIONS = Object.freeze([
|
package/dist/project.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":";;;AAAA,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,UAAU,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAC,oCAAoC;AAC9F,CAAC;AAED,IAAiB,OAAO,
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":";;;AAAA,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,UAAU,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,CAAC,oCAAoC;AAC9F,CAAC;AAED,IAAiB,OAAO,CAoJvB;AApJD,WAAiB,OAAO;IAqBtB;;;;;;;OAOG;IACH,SAAgB,WAAW;QACzB,OAAO,IAAa,CAAC;IACvB,CAAC;IAFe,mBAAW,cAE1B,CAAA;IAED,MAAM,QAAQ,GAA4C;QACxD,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC;QACnC,eAAe,EAAE,CAAC,MAAM,CAAC;QACzB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC;QACnC,eAAe,EAAE,CAAC,MAAM,CAAC;KAC1B,CAAC;IAEF;;;;;;;;;;OAUG;IACH,SAAgB,eAAe,CAAC,SAAuB;QACrD,OAAO,SAAS;aACb,MAAM,CACL,CAAC,aAAa,EAAE,YAAY,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAK,EAAkB,CAAC,EACpG,EAAE,CACH;aACA,IAAI,EAAE,CAAC;IACZ,CAAC;IAPe,uBAAe,kBAO9B,CAAA;IAED;;;;;;;OAOG;IACH,SAAgB,gBAAgB;QAC9B,OAAO,eAAe,CAAC,CAAC,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7F,CAAC;IAFe,wBAAgB,mBAE/B,CAAA;IAED,MAAM,mBAAmB,GAAyB,MAAM,CAAC,MAAM,CAAC;QAC9D,MAAM;QACN,OAAO;QACP,OAAO;QACP,OAAO;QACP,MAAM;QACN,MAAM;QACN,MAAM;QACN,OAAO;QACP,MAAM;QACN,MAAM;QACN,UAAU;QACV,MAAM;QACN,OAAO;KACR,CAAC,CAAC;IAEH;;;;;;;OAOG;IACH,SAAgB,kBAAkB;QAChC,OAAO,mBAAmB,CAAC;IAC7B,CAAC;IAFe,0BAAkB,qBAEjC,CAAA;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,eAAe;QACf,QAAQ;QACR,MAAM;QACN,WAAW;QACX,OAAO;QACP,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;KACP,CAAC,CAAC;IAEH;;;;;;;OAOG;IACH,SAAgB,OAAO;QACrB,OAAO,OAAO,CAAC;IACjB,CAAC;IAFe,eAAO,UAEtB,CAAA;IAED;;;;;;;OAOG;IACH,SAAgB,mBAAmB,CAAC,UAAgC;QAClE,OAAO,IAAI,MAAM,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC;IAFe,2BAAmB,sBAElC,CAAA;IAED;;;;;;;OAOG;IACH,SAAgB,gBAAgB,CAAC,UAAgC;QAC/D,OAAO,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACzE,CAAC;IAFe,wBAAgB,mBAE/B,CAAA;AACH,CAAC,EApJgB,OAAO,uBAAP,OAAO,QAoJvB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@w5s/dev",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Shared development constants and functions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"config",
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "40aa9e728c83ee50a5a381817c59df91458cf6a0"
|
|
47
47
|
}
|
package/src/block.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { type FileOptions, file, fileSync } from './file.js';
|
|
2
|
+
|
|
3
|
+
export interface BlockOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The marker builder function that will take either `markerBegin` or `markerEnd`
|
|
6
|
+
*
|
|
7
|
+
* @default '# ${mark} MANAGED BLOCK'
|
|
8
|
+
*/
|
|
9
|
+
marker?: (mark: 'Begin' | 'End') => string;
|
|
10
|
+
/**
|
|
11
|
+
* File path
|
|
12
|
+
*/
|
|
13
|
+
path: string;
|
|
14
|
+
/**
|
|
15
|
+
* Block content to insert
|
|
16
|
+
*/
|
|
17
|
+
block: string;
|
|
18
|
+
/**
|
|
19
|
+
* Insert position
|
|
20
|
+
*/
|
|
21
|
+
insertPosition?: ['before', 'BeginningOfFile'] | ['after', 'EndOfFile'];
|
|
22
|
+
/**
|
|
23
|
+
* Block target state
|
|
24
|
+
*/
|
|
25
|
+
state?: 'present' | 'absent';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const EOF = 'EndOfFile';
|
|
29
|
+
const BOF = 'BeginningOfFile';
|
|
30
|
+
|
|
31
|
+
function toFileOptions(options: BlockOptions): FileOptions {
|
|
32
|
+
const {
|
|
33
|
+
marker = (mark) => `# ${mark.toUpperCase()} MANAGED BLOCK`,
|
|
34
|
+
path,
|
|
35
|
+
block: blockName,
|
|
36
|
+
insertPosition = ['after', EOF],
|
|
37
|
+
state = 'present',
|
|
38
|
+
} = options;
|
|
39
|
+
|
|
40
|
+
const EOL = '\n';
|
|
41
|
+
const beginBlock = marker('Begin');
|
|
42
|
+
const endBlock = marker('End');
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param content
|
|
46
|
+
*/
|
|
47
|
+
function findBlock(content: string) {
|
|
48
|
+
const startIndex = content.indexOf(beginBlock);
|
|
49
|
+
const endIndex = content.indexOf(endBlock) + endBlock.length;
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
endIndex,
|
|
53
|
+
exists: startIndex >= 0 && endIndex >= 0,
|
|
54
|
+
startIndex,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function apply(fullContent: string, blockContent: string) {
|
|
59
|
+
const found = findBlock(fullContent);
|
|
60
|
+
const remove = state === 'absent';
|
|
61
|
+
const replaceBlock = remove ? '' : beginBlock + EOL + blockContent + EOL + endBlock;
|
|
62
|
+
const [positionDirection, positionAnchor] = insertPosition;
|
|
63
|
+
|
|
64
|
+
if (found.exists) {
|
|
65
|
+
return fullContent.slice(0, found.startIndex) + replaceBlock + fullContent.slice(found.endIndex);
|
|
66
|
+
}
|
|
67
|
+
if (remove) {
|
|
68
|
+
return fullContent;
|
|
69
|
+
}
|
|
70
|
+
switch (positionDirection) {
|
|
71
|
+
case 'after': {
|
|
72
|
+
// insert
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
74
|
+
if (positionAnchor === EOF) {
|
|
75
|
+
return fullContent + EOL + replaceBlock;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return fullContent;
|
|
79
|
+
}
|
|
80
|
+
case 'before': {
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
82
|
+
if (positionAnchor === BOF) {
|
|
83
|
+
return replaceBlock + EOL + fullContent;
|
|
84
|
+
}
|
|
85
|
+
return fullContent;
|
|
86
|
+
}
|
|
87
|
+
default: {
|
|
88
|
+
throw new Error(`Unsupported position ${String(positionDirection)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
path,
|
|
95
|
+
state: 'present',
|
|
96
|
+
update: (sourceContent) => apply(sourceContent, blockName),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Replace asynchronously a block in file that follows pattern :
|
|
102
|
+
*
|
|
103
|
+
* marker(markerBegin)
|
|
104
|
+
* ...
|
|
105
|
+
* marker(markerEnd)
|
|
106
|
+
*
|
|
107
|
+
* @param options
|
|
108
|
+
*/
|
|
109
|
+
export function block(options: BlockOptions) {
|
|
110
|
+
return file(toFileOptions(options));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Replace synchronously a block in file that follows pattern :
|
|
115
|
+
*
|
|
116
|
+
* marker(markerBegin)
|
|
117
|
+
* ...
|
|
118
|
+
* marker(markerEnd)
|
|
119
|
+
*
|
|
120
|
+
* @param options
|
|
121
|
+
*/
|
|
122
|
+
export function blockSync(options: BlockOptions) {
|
|
123
|
+
return fileSync(toFileOptions(options));
|
|
124
|
+
}
|
package/src/file.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { readFile, rm, writeFile, access } from 'node:fs/promises';
|
|
2
|
+
import { accessSync, constants, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
async function exists(path: string) {
|
|
5
|
+
try {
|
|
6
|
+
await access(path, constants.F_OK);
|
|
7
|
+
return true;
|
|
8
|
+
} catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function existsSync(path: string) {
|
|
14
|
+
try {
|
|
15
|
+
accessSync(path, constants.F_OK);
|
|
16
|
+
return true;
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface FileOptions {
|
|
23
|
+
/**
|
|
24
|
+
* File path
|
|
25
|
+
*/
|
|
26
|
+
readonly path: string;
|
|
27
|
+
/**
|
|
28
|
+
* File target state
|
|
29
|
+
*/
|
|
30
|
+
readonly state: 'present' | 'absent';
|
|
31
|
+
/**
|
|
32
|
+
* File content mapping function
|
|
33
|
+
*
|
|
34
|
+
* @param content
|
|
35
|
+
*/
|
|
36
|
+
readonly update?: (content: string) => string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* File encoding
|
|
39
|
+
*/
|
|
40
|
+
readonly encoding?: BufferEncoding;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Ensure file is present/absent with content initialized or modified with `update
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* await file({
|
|
49
|
+
* path: 'foo/bar',
|
|
50
|
+
* state: 'present',
|
|
51
|
+
* update: (content) => content + '_test', // This will append '_test' after current content
|
|
52
|
+
* })
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @param options
|
|
56
|
+
*/
|
|
57
|
+
export async function file(options: FileOptions): Promise<void> {
|
|
58
|
+
const { path, state, update, encoding = 'utf8' } = options;
|
|
59
|
+
if (state === 'present') {
|
|
60
|
+
const isPresent = await exists(path);
|
|
61
|
+
const previousContent = isPresent ? await readFile(path, encoding) : '';
|
|
62
|
+
const newContent = update == null ? '' : update(previousContent);
|
|
63
|
+
if (newContent != null) {
|
|
64
|
+
await writeFile(path, newContent, encoding);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
await rm(path);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Ensure file is present/absent with content initialized or modified with `update
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* fileSync({
|
|
77
|
+
* path: 'foo/bar',
|
|
78
|
+
* state: 'present',
|
|
79
|
+
* update: (content) => content + '_test', // This will append '_test' after current content
|
|
80
|
+
* })
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @param options
|
|
84
|
+
*/
|
|
85
|
+
export function fileSync(options: FileOptions): void {
|
|
86
|
+
const { path, state, update, encoding = 'utf8' } = options;
|
|
87
|
+
if (state === 'present') {
|
|
88
|
+
const isPresent = existsSync(path);
|
|
89
|
+
const previousContent = isPresent ? readFileSync(path, encoding) : '';
|
|
90
|
+
const newContent = update == null ? '' : update(previousContent);
|
|
91
|
+
if (newContent != null) {
|
|
92
|
+
writeFileSync(path, newContent, encoding);
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
rmSync(path);
|
|
96
|
+
}
|
|
97
|
+
}
|
package/src/index.ts
CHANGED
package/src/json.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type FileOptions, file, fileSync } from './file.js';
|
|
2
|
+
|
|
3
|
+
export type JSONValue = null | number | string | boolean | JSONValue[] | { [key: string]: JSONValue };
|
|
4
|
+
|
|
5
|
+
export interface JSONOption<V = JSONValue> {
|
|
6
|
+
/**
|
|
7
|
+
* File path
|
|
8
|
+
*/
|
|
9
|
+
readonly path: string;
|
|
10
|
+
/**
|
|
11
|
+
* File target state
|
|
12
|
+
*/
|
|
13
|
+
readonly state: 'present' | 'absent';
|
|
14
|
+
/**
|
|
15
|
+
* File content mapping function
|
|
16
|
+
*
|
|
17
|
+
* @param content
|
|
18
|
+
*/
|
|
19
|
+
readonly update?: (content: V | undefined) => V | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* File encoding
|
|
22
|
+
*/
|
|
23
|
+
readonly encoding?: BufferEncoding;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function toFileOption<Value>({ update, ...otherOptions }: JSONOption<Value>): FileOptions {
|
|
27
|
+
return {
|
|
28
|
+
...otherOptions,
|
|
29
|
+
|
|
30
|
+
update:
|
|
31
|
+
update == null
|
|
32
|
+
? update
|
|
33
|
+
: (content) => {
|
|
34
|
+
const jsonValue = content === '' ? undefined : (JSON.parse(content) as Value);
|
|
35
|
+
|
|
36
|
+
return JSON.stringify(update(jsonValue));
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Ensure file is present/absent asynchronously with content value initialized or modified with `update`
|
|
43
|
+
*
|
|
44
|
+
* @param options
|
|
45
|
+
*/
|
|
46
|
+
export async function json<Value>(options: JSONOption<Value>): Promise<void> {
|
|
47
|
+
return file(toFileOption(options));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Ensure file is present/absent synchronously with content value initialized or modified with `update`
|
|
52
|
+
*
|
|
53
|
+
* @param options
|
|
54
|
+
*/
|
|
55
|
+
export function jsonSync<Value>(options: JSONOption<Value>): void {
|
|
56
|
+
return fileSync(toFileOption(options));
|
|
57
|
+
}
|
package/src/project.ts
CHANGED
|
@@ -3,8 +3,26 @@ function escapeRegExp(value: string) {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
export namespace Project {
|
|
6
|
+
/**
|
|
7
|
+
* A type of a file extension
|
|
8
|
+
*/
|
|
6
9
|
export type Extension = `.${string}`;
|
|
7
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Object hash of all well-known file extension category to file extensions mapping
|
|
13
|
+
*/
|
|
14
|
+
export interface ExtensionRegistry {
|
|
15
|
+
javascript: readonly Extension[];
|
|
16
|
+
javascriptreact: readonly Extension[];
|
|
17
|
+
typescript: readonly Extension[];
|
|
18
|
+
typescriptreact: readonly Extension[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A list of "vscode-like" language identifiers (i.e. "javascript", "javascriptreact")
|
|
23
|
+
*/
|
|
24
|
+
export type LanguageId = keyof ExtensionRegistry;
|
|
25
|
+
|
|
8
26
|
/**
|
|
9
27
|
* Supported ECMA version
|
|
10
28
|
*
|
|
@@ -17,16 +35,32 @@ export namespace Project {
|
|
|
17
35
|
return 2022 as const;
|
|
18
36
|
}
|
|
19
37
|
|
|
20
|
-
const
|
|
21
|
-
'.
|
|
22
|
-
'.
|
|
23
|
-
'.cts',
|
|
24
|
-
'.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
const registry: { [key: string]: readonly Extension[] } = {
|
|
39
|
+
javascript: ['.js', '.cjs', '.mjs'],
|
|
40
|
+
javascriptreact: ['.jsx'],
|
|
41
|
+
typescript: ['.ts', '.cts', '.mts'],
|
|
42
|
+
typescriptreact: ['.tsx'],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Return a list of extensions
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* Project.queryExtensions(['javascript']); // ['.js', '.cjs', ...]
|
|
51
|
+
* Project.queryExtensions(['typescript', 'typescriptreact']); // ['.ts', '.mts', ..., '.tsx']
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @param languages
|
|
55
|
+
*/
|
|
56
|
+
export function queryExtensions(languages: LanguageId[]): readonly Extension[] {
|
|
57
|
+
return languages
|
|
58
|
+
.reduce<Extension[]>(
|
|
59
|
+
(previousValue, currentValue) => previousValue.concat(registry[currentValue] ?? ([] as Extension[])),
|
|
60
|
+
[]
|
|
61
|
+
)
|
|
62
|
+
.sort();
|
|
63
|
+
}
|
|
30
64
|
|
|
31
65
|
/**
|
|
32
66
|
* Supported file extensions
|
|
@@ -37,7 +71,7 @@ export namespace Project {
|
|
|
37
71
|
* ```
|
|
38
72
|
*/
|
|
39
73
|
export function sourceExtensions() {
|
|
40
|
-
return
|
|
74
|
+
return queryExtensions(['javascript', 'javascriptreact', 'typescript', 'typescriptreact']);
|
|
41
75
|
}
|
|
42
76
|
|
|
43
77
|
const RESOURCE_EXTENSIONS: readonly Extension[] = Object.freeze([
|