lfify 1.1.0 → 1.1.1
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/.github/workflows/cd.yml +3 -6
- package/.releaserc.json +21 -0
- package/README.md +49 -13
- package/__mocks__/fs.js +3 -1
- package/__mocks__/micromatch.js +22 -6
- package/index.cjs +134 -5
- package/index.test.js +198 -8
- package/package.json +11 -4
package/.github/workflows/cd.yml
CHANGED
|
@@ -4,13 +4,11 @@ on:
|
|
|
4
4
|
branches:
|
|
5
5
|
- main
|
|
6
6
|
|
|
7
|
-
permissions:
|
|
8
|
-
contents: read # for checkout
|
|
9
|
-
|
|
10
7
|
jobs:
|
|
11
8
|
release:
|
|
12
9
|
name: Release
|
|
13
10
|
runs-on: ubuntu-latest
|
|
11
|
+
environment: npm
|
|
14
12
|
permissions:
|
|
15
13
|
contents: write # to be able to publish a GitHub release
|
|
16
14
|
issues: write # to be able to comment on released issues
|
|
@@ -22,7 +20,7 @@ jobs:
|
|
|
22
20
|
with:
|
|
23
21
|
fetch-depth: 0
|
|
24
22
|
- name: Setup Node.js
|
|
25
|
-
uses: actions/setup-node@
|
|
23
|
+
uses: actions/setup-node@v6
|
|
26
24
|
with:
|
|
27
25
|
node-version: "lts/*"
|
|
28
26
|
- name: Install dependencies
|
|
@@ -32,5 +30,4 @@ jobs:
|
|
|
32
30
|
- name: Release
|
|
33
31
|
env:
|
|
34
32
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
35
|
-
|
|
36
|
-
run: npx semantic-release
|
|
33
|
+
run: NODE_AUTH_TOKEN="" npx semantic-release
|
package/.releaserc.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"branches": ["main"],
|
|
3
|
+
"plugins": [
|
|
4
|
+
"@semantic-release/commit-analyzer",
|
|
5
|
+
"@semantic-release/release-notes-generator",
|
|
6
|
+
[
|
|
7
|
+
"@semantic-release/changelog",
|
|
8
|
+
{
|
|
9
|
+
"changelogFile": "CHANGELOG.md"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"@semantic-release/npm",
|
|
13
|
+
[
|
|
14
|
+
"@semantic-release/git",
|
|
15
|
+
{
|
|
16
|
+
"assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
|
|
17
|
+
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
]
|
|
21
|
+
}
|
package/README.md
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
# LFify
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
A lightweight Node.js library to convert CRLF to LF line endings.
|
|
6
|
-
It is useful when your development environment is Windows.
|
|
3
|
+
A lightweight Node.js program to convert CRLF to LF line endings.
|
|
4
|
+
It is useful when your development environment is Windows.
|
|
7
5
|
|
|
8
6
|
## Getting started
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
### Using CLI options (no config file needed)
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx lfify --include "**/*.js" --exclude "node_modules/**"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Using config file
|
|
15
|
+
|
|
16
|
+
Create `.lfifyrc.json`:
|
|
11
17
|
|
|
12
18
|
```json
|
|
13
19
|
{
|
|
@@ -25,22 +31,52 @@ create .lfifyrc.json
|
|
|
25
31
|
"build/**",
|
|
26
32
|
"coverage/**"
|
|
27
33
|
]
|
|
28
|
-
}
|
|
34
|
+
}
|
|
29
35
|
```
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
Then run:
|
|
32
38
|
|
|
33
39
|
```bash
|
|
34
|
-
npx
|
|
40
|
+
npx lfify
|
|
35
41
|
```
|
|
36
42
|
|
|
37
|
-
you can add options below.
|
|
38
|
-
|
|
39
43
|
## Options
|
|
40
44
|
|
|
41
|
-
| Option
|
|
42
|
-
|
|
43
|
-
| `--config <path>`
|
|
45
|
+
| Option | Description |
|
|
46
|
+
| -------------------- | --------------------------------------------------------------------------- |
|
|
47
|
+
| `--config <path>` | Specify a custom path for the configuration file. Default is `.lfifyrc.json`. |
|
|
48
|
+
| `--entry <path>` | Specify the entry directory to process. Default is `./`. |
|
|
49
|
+
| `--include <pattern>`| Glob pattern(s) to include. Can be used multiple times. |
|
|
50
|
+
| `--exclude <pattern>`| Glob pattern(s) to exclude. Can be used multiple times. |
|
|
51
|
+
|
|
52
|
+
## Examples
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Process all JavaScript files, exclude node_modules
|
|
56
|
+
npx lfify --include "**/*.js" --exclude "node_modules/**"
|
|
57
|
+
|
|
58
|
+
# Process multiple file types
|
|
59
|
+
npx lfify --include "**/*.js" --include "**/*.ts" --exclude "node_modules/**" --exclude ".git/**"
|
|
60
|
+
|
|
61
|
+
# Process files in a specific directory
|
|
62
|
+
npx lfify --entry ./src --include "**/*.js"
|
|
63
|
+
|
|
64
|
+
# Use a custom config file
|
|
65
|
+
npx lfify --config ./custom-config.json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Default behavior
|
|
69
|
+
|
|
70
|
+
When no config file is found and no CLI options are provided, lfify uses sensible defaults:
|
|
71
|
+
- **include**: `**/*` (all files)
|
|
72
|
+
- **exclude**: `node_modules/**`, `.git/**`, `dist/**`, `build/**`, `coverage/**`
|
|
73
|
+
|
|
74
|
+
## Priority
|
|
75
|
+
|
|
76
|
+
CLI options take precedence over config file values:
|
|
77
|
+
1. CLI arguments (highest)
|
|
78
|
+
2. Config file
|
|
79
|
+
3. Default values (lowest)
|
|
44
80
|
|
|
45
81
|
# Development
|
|
46
82
|
|
package/__mocks__/fs.js
CHANGED
|
@@ -19,7 +19,9 @@ const promises = {
|
|
|
19
19
|
if (mockFiles.has(path)) {
|
|
20
20
|
return Promise.resolve(mockFiles.get(path));
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
const error = new Error(`ENOENT: no such file or directory, open '${path}'`);
|
|
23
|
+
error.code = 'ENOENT';
|
|
24
|
+
return Promise.reject(error);
|
|
23
25
|
}),
|
|
24
26
|
|
|
25
27
|
writeFile: jest.fn().mockImplementation((path, content) => {
|
package/__mocks__/micromatch.js
CHANGED
|
@@ -10,12 +10,28 @@ micromatch.isMatch = jest.fn().mockImplementation((filePath, patterns) => {
|
|
|
10
10
|
// 정확한 매칭
|
|
11
11
|
if (pattern === filePath) return true;
|
|
12
12
|
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
// glob 패턴을 정규식으로 변환
|
|
14
|
+
// 플레이스홀더를 사용해서 순서 문제 해결
|
|
15
|
+
const GLOBSTAR_SLASH = '___GLOBSTARSLASH___';
|
|
16
|
+
const GLOBSTAR = '___GLOBSTAR___';
|
|
17
|
+
|
|
18
|
+
let regexPattern = pattern
|
|
19
|
+
// **/ 패턴을 플레이스홀더로 임시 변환
|
|
20
|
+
.replace(/\*\*\//g, GLOBSTAR_SLASH)
|
|
21
|
+
// ** 패턴을 플레이스홀더로 임시 변환
|
|
22
|
+
.replace(/\*\*/g, GLOBSTAR)
|
|
23
|
+
// {a,b} 패턴
|
|
24
|
+
.replace(/\{([^}]+)\}/g, (_, group) => `(${group.split(',').join('|')})`)
|
|
25
|
+
// 특수문자 이스케이프 (*, ?, / 제외)
|
|
26
|
+
.replace(/[.+^$|()[\]\\]/g, '\\$&')
|
|
27
|
+
// * 패턴: 슬래시를 제외한 모든 것 매칭
|
|
28
|
+
.replace(/\*/g, '[^/]*')
|
|
29
|
+
// ? 패턴: 슬래시를 제외한 한 문자 매칭
|
|
30
|
+
.replace(/\?/g, '[^/]')
|
|
31
|
+
// 플레이스홀더를 실제 정규식으로 변환
|
|
32
|
+
.replace(new RegExp(GLOBSTAR_SLASH, 'g'), '(?:.*/)?')
|
|
33
|
+
.replace(new RegExp(GLOBSTAR, 'g'), '.*');
|
|
34
|
+
|
|
19
35
|
return new RegExp(`^${regexPattern}$`).test(filePath);
|
|
20
36
|
});
|
|
21
37
|
});
|
package/index.cjs
CHANGED
|
@@ -20,7 +20,10 @@ const micromatch = require("micromatch");
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* @typedef {Object} CommandOptions
|
|
23
|
-
* @property {string} configPath - 설정 파일 경로
|
|
23
|
+
* @property {string} [configPath] - 설정 파일 경로
|
|
24
|
+
* @property {string} [entry] - CLI로 지정한 entry 경로
|
|
25
|
+
* @property {string[]} [include] - CLI로 지정한 include 패턴
|
|
26
|
+
* @property {string[]} [exclude] - CLI로 지정한 exclude 패턴
|
|
24
27
|
*/
|
|
25
28
|
|
|
26
29
|
/**
|
|
@@ -33,6 +36,22 @@ const DEFAULT_CONFIG = {
|
|
|
33
36
|
exclude: []
|
|
34
37
|
};
|
|
35
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Sensible defaults when no config file is provided
|
|
41
|
+
* @type {Config}
|
|
42
|
+
*/
|
|
43
|
+
const SENSIBLE_DEFAULTS = {
|
|
44
|
+
entry: './',
|
|
45
|
+
include: ['**/*'],
|
|
46
|
+
exclude: [
|
|
47
|
+
'node_modules/**',
|
|
48
|
+
'.git/**',
|
|
49
|
+
'dist/**',
|
|
50
|
+
'build/**',
|
|
51
|
+
'coverage/**'
|
|
52
|
+
]
|
|
53
|
+
};
|
|
54
|
+
|
|
36
55
|
/**
|
|
37
56
|
* Configuration validation schema
|
|
38
57
|
* @type {Object.<string, function(*): boolean>}
|
|
@@ -90,6 +109,83 @@ async function readConfig(configPath) {
|
|
|
90
109
|
}
|
|
91
110
|
}
|
|
92
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Resolve final configuration from CLI options, config file, and defaults
|
|
114
|
+
* @param {CommandOptions} cliOptions - parsed CLI options
|
|
115
|
+
* @returns {Promise<Config>} - resolved configuration
|
|
116
|
+
*/
|
|
117
|
+
async function resolveConfig(cliOptions) {
|
|
118
|
+
let fileConfig = null;
|
|
119
|
+
|
|
120
|
+
// Try to load config file if it exists
|
|
121
|
+
if (cliOptions.configPath) {
|
|
122
|
+
try {
|
|
123
|
+
const configContent = await fs.readFile(cliOptions.configPath, 'utf8');
|
|
124
|
+
fileConfig = JSON.parse(configContent);
|
|
125
|
+
|
|
126
|
+
// Validate config file fields
|
|
127
|
+
for (const [key, validator] of Object.entries(CONFIG_SCHEMA)) {
|
|
128
|
+
if (fileConfig[key] && !validator(fileConfig[key])) {
|
|
129
|
+
throw new Error(`Invalid "${key}" in configuration file`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
if (err.code !== 'ENOENT') {
|
|
134
|
+
// Re-throw parsing/validation errors
|
|
135
|
+
logger.error(`Error reading configuration file: ${err.message}`, cliOptions.configPath);
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
// ENOENT is okay - config file is optional now
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Determine final values with precedence: CLI > config file > defaults
|
|
143
|
+
const hasCLIInclude = Array.isArray(cliOptions.include) && cliOptions.include.length > 0;
|
|
144
|
+
const hasCLIExclude = Array.isArray(cliOptions.exclude) && cliOptions.exclude.length > 0;
|
|
145
|
+
const hasCLIEntry = typeof cliOptions.entry === 'string';
|
|
146
|
+
|
|
147
|
+
const hasFileConfig = fileConfig !== null;
|
|
148
|
+
const hasFileInclude = hasFileConfig && Array.isArray(fileConfig.include) && fileConfig.include.length > 0;
|
|
149
|
+
const hasFileExclude = hasFileConfig && Array.isArray(fileConfig.exclude) && fileConfig.exclude.length > 0;
|
|
150
|
+
const hasFileEntry = hasFileConfig && typeof fileConfig.entry === 'string';
|
|
151
|
+
|
|
152
|
+
// Resolve each config property
|
|
153
|
+
let include, exclude, entry;
|
|
154
|
+
|
|
155
|
+
// Include: CLI > file > default
|
|
156
|
+
if (hasCLIInclude) {
|
|
157
|
+
include = cliOptions.include;
|
|
158
|
+
} else if (hasFileInclude) {
|
|
159
|
+
include = fileConfig.include;
|
|
160
|
+
} else {
|
|
161
|
+
include = SENSIBLE_DEFAULTS.include;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Exclude: CLI > file > default
|
|
165
|
+
if (hasCLIExclude) {
|
|
166
|
+
exclude = cliOptions.exclude;
|
|
167
|
+
} else if (hasFileExclude) {
|
|
168
|
+
exclude = fileConfig.exclude;
|
|
169
|
+
} else {
|
|
170
|
+
exclude = SENSIBLE_DEFAULTS.exclude;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Entry: CLI > file > default
|
|
174
|
+
if (hasCLIEntry) {
|
|
175
|
+
entry = cliOptions.entry;
|
|
176
|
+
} else if (hasFileEntry) {
|
|
177
|
+
entry = fileConfig.entry;
|
|
178
|
+
} else {
|
|
179
|
+
entry = SENSIBLE_DEFAULTS.entry;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
entry: path.resolve(process.cwd(), entry),
|
|
184
|
+
include,
|
|
185
|
+
exclude
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
93
189
|
/**
|
|
94
190
|
* Parse command line arguments
|
|
95
191
|
* @returns {CommandOptions} - parsed arguments
|
|
@@ -101,9 +197,39 @@ function parseArgs() {
|
|
|
101
197
|
};
|
|
102
198
|
|
|
103
199
|
for (let i = 0; i < args.length; i++) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
200
|
+
const arg = args[i];
|
|
201
|
+
const nextArg = args[i + 1];
|
|
202
|
+
|
|
203
|
+
switch (arg) {
|
|
204
|
+
case '--config':
|
|
205
|
+
if (nextArg) {
|
|
206
|
+
options.configPath = nextArg;
|
|
207
|
+
i++;
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
|
|
211
|
+
case '--entry':
|
|
212
|
+
if (nextArg) {
|
|
213
|
+
options.entry = nextArg;
|
|
214
|
+
i++;
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
|
|
218
|
+
case '--include':
|
|
219
|
+
if (nextArg) {
|
|
220
|
+
options.include = options.include || [];
|
|
221
|
+
options.include.push(nextArg);
|
|
222
|
+
i++;
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
225
|
+
|
|
226
|
+
case '--exclude':
|
|
227
|
+
if (nextArg) {
|
|
228
|
+
options.exclude = options.exclude || [];
|
|
229
|
+
options.exclude.push(nextArg);
|
|
230
|
+
i++;
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
107
233
|
}
|
|
108
234
|
}
|
|
109
235
|
|
|
@@ -184,7 +310,7 @@ async function processFile(filePath) {
|
|
|
184
310
|
|
|
185
311
|
async function main() {
|
|
186
312
|
const options = parseArgs();
|
|
187
|
-
const config = await
|
|
313
|
+
const config = await resolveConfig(options);
|
|
188
314
|
|
|
189
315
|
logger.info(`converting CRLF to LF in: ${config.entry}`, config.entry);
|
|
190
316
|
|
|
@@ -202,4 +328,7 @@ module.exports = {
|
|
|
202
328
|
processFile,
|
|
203
329
|
readConfig,
|
|
204
330
|
parseArgs,
|
|
331
|
+
resolveConfig,
|
|
332
|
+
shouldProcessFile,
|
|
333
|
+
SENSIBLE_DEFAULTS,
|
|
205
334
|
};
|
package/index.test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { readConfig, parseArgs, processFile } = require('./index.cjs');
|
|
1
|
+
const { readConfig, parseArgs, processFile, resolveConfig, shouldProcessFile, SENSIBLE_DEFAULTS } = require('./index.cjs');
|
|
2
2
|
|
|
3
3
|
jest.mock('fs');
|
|
4
4
|
jest.mock('path');
|
|
@@ -16,10 +16,17 @@ describe('CRLF to LF Converter', () => {
|
|
|
16
16
|
'./node_modules/subdir/file4.txt': 'test\r\n',
|
|
17
17
|
'index.js': 'console.log("test");\r\n'
|
|
18
18
|
};
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
let originalArgv;
|
|
21
|
+
|
|
20
22
|
beforeEach(() => {
|
|
21
23
|
jest.clearAllMocks();
|
|
22
24
|
require('fs').__setMockFiles(MOCK_FILE_INFO);
|
|
25
|
+
originalArgv = process.argv;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
process.argv = originalArgv;
|
|
23
30
|
});
|
|
24
31
|
|
|
25
32
|
describe('readConfig', () => {
|
|
@@ -79,15 +86,108 @@ describe('CRLF to LF Converter', () => {
|
|
|
79
86
|
// assert
|
|
80
87
|
expect(options.configPath).toBe('.lfifyrc.json');
|
|
81
88
|
});
|
|
89
|
+
|
|
90
|
+
it('should return include patterns when single --include option is provided', () => {
|
|
91
|
+
process.argv = ['node', 'lfify', '--include', '**/*.js'];
|
|
92
|
+
const options = parseArgs();
|
|
93
|
+
expect(options.include).toEqual(['**/*.js']);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should return multiple include patterns when multiple --include options are provided', () => {
|
|
97
|
+
process.argv = ['node', 'lfify', '--include', '**/*.js', '--include', '**/*.ts'];
|
|
98
|
+
const options = parseArgs();
|
|
99
|
+
expect(options.include).toEqual(['**/*.js', '**/*.ts']);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should return exclude patterns when --exclude option is provided', () => {
|
|
103
|
+
process.argv = ['node', 'lfify', '--exclude', 'node_modules/**'];
|
|
104
|
+
const options = parseArgs();
|
|
105
|
+
expect(options.exclude).toEqual(['node_modules/**']);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should return multiple exclude patterns when multiple --exclude options are provided', () => {
|
|
109
|
+
process.argv = ['node', 'lfify', '--exclude', 'node_modules/**', '--exclude', '.git/**'];
|
|
110
|
+
const options = parseArgs();
|
|
111
|
+
expect(options.exclude).toEqual(['node_modules/**', '.git/**']);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should return entry path when --entry option is provided', () => {
|
|
115
|
+
process.argv = ['node', 'lfify', '--entry', './src'];
|
|
116
|
+
const options = parseArgs();
|
|
117
|
+
expect(options.entry).toBe('./src');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should handle all options together', () => {
|
|
121
|
+
process.argv = [
|
|
122
|
+
'node', 'lfify',
|
|
123
|
+
'--entry', './src',
|
|
124
|
+
'--include', '**/*.js',
|
|
125
|
+
'--include', '**/*.ts',
|
|
126
|
+
'--exclude', 'node_modules/**',
|
|
127
|
+
'--config', 'custom.json'
|
|
128
|
+
];
|
|
129
|
+
const options = parseArgs();
|
|
130
|
+
expect(options).toEqual({
|
|
131
|
+
configPath: 'custom.json',
|
|
132
|
+
entry: './src',
|
|
133
|
+
include: ['**/*.js', '**/*.ts'],
|
|
134
|
+
exclude: ['node_modules/**']
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should return undefined for include/exclude/entry when not provided', () => {
|
|
139
|
+
process.argv = ['node', 'lfify'];
|
|
140
|
+
const options = parseArgs();
|
|
141
|
+
expect(options.include).toBeUndefined();
|
|
142
|
+
expect(options.exclude).toBeUndefined();
|
|
143
|
+
expect(options.entry).toBeUndefined();
|
|
144
|
+
});
|
|
82
145
|
});
|
|
83
146
|
|
|
84
147
|
describe('shouldProcessFile', () => {
|
|
85
148
|
it('should return true when file matches include pattern and does not match exclude pattern', () => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
149
|
+
const config = {
|
|
150
|
+
include: ['**/*.js'],
|
|
151
|
+
exclude: ['node_modules/**']
|
|
152
|
+
};
|
|
153
|
+
expect(shouldProcessFile('src/file.js', config)).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should return false when file matches exclude pattern', () => {
|
|
157
|
+
const config = {
|
|
158
|
+
include: ['**/*.js'],
|
|
159
|
+
exclude: ['node_modules/**']
|
|
160
|
+
};
|
|
161
|
+
expect(shouldProcessFile('node_modules/package/index.js', config)).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should return false when file does not match include pattern', () => {
|
|
165
|
+
const config = {
|
|
166
|
+
include: ['**/*.js'],
|
|
167
|
+
exclude: ['node_modules/**']
|
|
168
|
+
};
|
|
169
|
+
expect(shouldProcessFile('src/file.txt', config)).toBe(false);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should handle multiple include patterns', () => {
|
|
173
|
+
const config = {
|
|
174
|
+
include: ['**/*.js', '**/*.ts'],
|
|
175
|
+
exclude: []
|
|
176
|
+
};
|
|
177
|
+
expect(shouldProcessFile('src/file.js', config)).toBe(true);
|
|
178
|
+
expect(shouldProcessFile('src/file.ts', config)).toBe(true);
|
|
179
|
+
expect(shouldProcessFile('src/file.txt', config)).toBe(false);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should handle multiple exclude patterns', () => {
|
|
183
|
+
const config = {
|
|
184
|
+
include: ['**/*.js'],
|
|
185
|
+
exclude: ['node_modules/**', 'dist/**', 'test/**']
|
|
186
|
+
};
|
|
187
|
+
expect(shouldProcessFile('src/file.js', config)).toBe(true);
|
|
188
|
+
expect(shouldProcessFile('node_modules/pkg/index.js', config)).toBe(false);
|
|
189
|
+
expect(shouldProcessFile('dist/bundle.js', config)).toBe(false);
|
|
190
|
+
expect(shouldProcessFile('test/unit.js', config)).toBe(false);
|
|
91
191
|
});
|
|
92
192
|
});
|
|
93
193
|
|
|
@@ -102,6 +202,96 @@ describe('CRLF to LF Converter', () => {
|
|
|
102
202
|
|
|
103
203
|
// assert
|
|
104
204
|
expect(content).toBe(shouldbe);
|
|
105
|
-
})
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('should not modify file when no CRLF exists', async () => {
|
|
208
|
+
// arrange
|
|
209
|
+
require('fs').__setMockFiles({ './src/clean.txt': 'hello\nworld\n' });
|
|
210
|
+
|
|
211
|
+
// act
|
|
212
|
+
await processFile('./src/clean.txt');
|
|
213
|
+
const content = await require('fs').promises.readFile('./src/clean.txt', 'utf8');
|
|
214
|
+
|
|
215
|
+
// assert
|
|
216
|
+
expect(content).toBe('hello\nworld\n');
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('resolveConfig', () => {
|
|
221
|
+
it('should use CLI options when provided without config file', async () => {
|
|
222
|
+
const options = {
|
|
223
|
+
include: ['**/*.js'],
|
|
224
|
+
exclude: ['node_modules/**'],
|
|
225
|
+
entry: './src'
|
|
226
|
+
};
|
|
227
|
+
const config = await resolveConfig(options);
|
|
228
|
+
|
|
229
|
+
expect(config.include).toEqual(['**/*.js']);
|
|
230
|
+
expect(config.exclude).toEqual(['node_modules/**']);
|
|
231
|
+
expect(config.entry).toContain('src');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should use sensible defaults when no config file and no CLI options', async () => {
|
|
235
|
+
const config = await resolveConfig({});
|
|
236
|
+
|
|
237
|
+
expect(config.include).toEqual(SENSIBLE_DEFAULTS.include);
|
|
238
|
+
expect(config.exclude).toEqual(SENSIBLE_DEFAULTS.exclude);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should override config file values with CLI options', async () => {
|
|
242
|
+
require('fs').__setConfig(JSON.stringify({
|
|
243
|
+
entry: './',
|
|
244
|
+
include: ['**/*.md'],
|
|
245
|
+
exclude: ['dist/**']
|
|
246
|
+
}));
|
|
247
|
+
|
|
248
|
+
const options = {
|
|
249
|
+
configPath: '.lfifyrc.json',
|
|
250
|
+
include: ['**/*.js'] // CLI should override config
|
|
251
|
+
};
|
|
252
|
+
const config = await resolveConfig(options);
|
|
253
|
+
|
|
254
|
+
expect(config.include).toEqual(['**/*.js']); // CLI override
|
|
255
|
+
expect(config.exclude).toEqual(['dist/**']); // from config file
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should load config file when configPath is provided and file exists', async () => {
|
|
259
|
+
require('fs').__setConfig(JSON.stringify({
|
|
260
|
+
entry: './lib',
|
|
261
|
+
include: ['**/*.ts'],
|
|
262
|
+
exclude: ['test/**']
|
|
263
|
+
}));
|
|
264
|
+
|
|
265
|
+
const options = { configPath: '.lfifyrc.json' };
|
|
266
|
+
const config = await resolveConfig(options);
|
|
267
|
+
|
|
268
|
+
expect(config.include).toEqual(['**/*.ts']);
|
|
269
|
+
expect(config.exclude).toEqual(['test/**']);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should use defaults when config file not found and no CLI options', async () => {
|
|
273
|
+
// No config file set up - mock will throw ENOENT
|
|
274
|
+
const options = { configPath: 'nonexistent.json' };
|
|
275
|
+
const config = await resolveConfig(options);
|
|
276
|
+
|
|
277
|
+
expect(config.include).toEqual(SENSIBLE_DEFAULTS.include);
|
|
278
|
+
expect(config.exclude).toEqual(SENSIBLE_DEFAULTS.exclude);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should use CLI include with default exclude when only include provided', async () => {
|
|
282
|
+
const options = { include: ['**/*.js'] };
|
|
283
|
+
const config = await resolveConfig(options);
|
|
284
|
+
|
|
285
|
+
expect(config.include).toEqual(['**/*.js']);
|
|
286
|
+
expect(config.exclude).toEqual(SENSIBLE_DEFAULTS.exclude);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should use CLI exclude with default include when only exclude provided', async () => {
|
|
290
|
+
const options = { exclude: ['custom/**'] };
|
|
291
|
+
const config = await resolveConfig(options);
|
|
292
|
+
|
|
293
|
+
expect(config.include).toEqual(SENSIBLE_DEFAULTS.include);
|
|
294
|
+
expect(config.exclude).toEqual(['custom/**']);
|
|
295
|
+
});
|
|
106
296
|
});
|
|
107
297
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lfify",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "make your crlf to lf",
|
|
6
6
|
"main": "index.cjs",
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
},
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
|
-
"url": "https://github.com/GyeongHoKim/lfify"
|
|
17
|
+
"url": "https://github.com/GyeongHoKim/lfify.git"
|
|
18
18
|
},
|
|
19
19
|
"publishConfig": {
|
|
20
|
-
"registry": "https://registry.npmjs.org"
|
|
20
|
+
"registry": "https://registry.npmjs.org",
|
|
21
|
+
"tag": "latest"
|
|
21
22
|
},
|
|
22
23
|
"keywords": [
|
|
23
24
|
"eol",
|
|
@@ -35,8 +36,14 @@
|
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@eslint/js": "^9.15.0",
|
|
39
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
40
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
41
|
+
"@semantic-release/git": "^10.0.1",
|
|
42
|
+
"@semantic-release/npm": "^13.1.3",
|
|
43
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
38
44
|
"eslint": "^9.15.0",
|
|
39
45
|
"globals": "^15.12.0",
|
|
40
|
-
"jest": "^29.7.0"
|
|
46
|
+
"jest": "^29.7.0",
|
|
47
|
+
"semantic-release": "^25.0.2"
|
|
41
48
|
}
|
|
42
49
|
}
|