lfify 1.2.0 → 1.2.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 +1 -1
- package/.github/workflows/ci.yml +26 -26
- package/.lfifyrc-sample.json +1 -1
- package/.prettierignore +5 -0
- package/.prettierrc +9 -0
- package/.vscode/extensions.json +3 -0
- package/.vscode/launch.json +2 -4
- package/.vscode/settings.json +16 -0
- package/CHANGELOG.md +7 -0
- package/README.md +8 -6
- package/__mocks__/path.js +1 -1
- package/eslint.config.mjs +6 -5
- package/index.cjs +68 -43
- package/index.e2e.test.js +18 -7
- package/index.test.js +105 -48
- package/package.json +7 -2
package/.github/workflows/cd.yml
CHANGED
|
@@ -22,7 +22,7 @@ jobs:
|
|
|
22
22
|
- name: Setup Node.js
|
|
23
23
|
uses: actions/setup-node@v6
|
|
24
24
|
with:
|
|
25
|
-
node-version:
|
|
25
|
+
node-version: '24'
|
|
26
26
|
- name: Install dependencies
|
|
27
27
|
run: npm clean-install
|
|
28
28
|
- name: Verify the integrity of provenance attestations and registry signatures for installed dependencies
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -2,41 +2,41 @@ name: Lint and Test
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
pull_request:
|
|
5
|
-
branches: [
|
|
5
|
+
branches: [main]
|
|
6
6
|
push:
|
|
7
|
-
branches: [
|
|
7
|
+
branches: [main]
|
|
8
8
|
|
|
9
9
|
jobs:
|
|
10
10
|
lint:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
|
|
13
13
|
steps:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
- uses: actions/checkout@v3
|
|
15
|
+
|
|
16
|
+
- name: Setup Node.js
|
|
17
|
+
uses: actions/setup-node@v3
|
|
18
|
+
with:
|
|
19
|
+
node-version: '18'
|
|
20
|
+
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: npm ci
|
|
23
|
+
|
|
24
|
+
- name: Run linting
|
|
25
|
+
run: npm run lint || true
|
|
26
26
|
|
|
27
27
|
test:
|
|
28
28
|
runs-on: ubuntu-latest
|
|
29
29
|
|
|
30
30
|
steps:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
- uses: actions/checkout@v3
|
|
32
|
+
|
|
33
|
+
- name: Setup Node.js
|
|
34
|
+
uses: actions/setup-node@v3
|
|
35
|
+
with:
|
|
36
|
+
node-version: '18'
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: npm ci
|
|
40
|
+
|
|
41
|
+
- name: Run tests
|
|
42
|
+
run: npm test
|
package/.lfifyrc-sample.json
CHANGED
package/.prettierignore
ADDED
package/.prettierrc
ADDED
package/.vscode/launch.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
3
|
+
"editor.formatOnSave": true,
|
|
4
|
+
"editor.codeActionsOnSave": {
|
|
5
|
+
"source.fixAll.eslint": "explicit"
|
|
6
|
+
},
|
|
7
|
+
"[javascript]": {
|
|
8
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
9
|
+
},
|
|
10
|
+
"[json]": {
|
|
11
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
12
|
+
},
|
|
13
|
+
"[markdown]": {
|
|
14
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.2.1](https://github.com/GyeongHoKim/lfify/compare/v1.2.0...v1.2.1) (2026-03-18)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* unlink tmp files when rename failed ([173a38f](https://github.com/GyeongHoKim/lfify/commit/173a38f026d2f37dc521a4a5e089d071cfd425c9))
|
|
7
|
+
|
|
1
8
|
# [1.2.0](https://github.com/GyeongHoKim/lfify/compare/v1.1.0...v1.2.0) (2026-03-16)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -42,12 +42,12 @@ npx lfify
|
|
|
42
42
|
|
|
43
43
|
## Options
|
|
44
44
|
|
|
45
|
-
| Option
|
|
46
|
-
|
|
|
47
|
-
| `--config <path>`
|
|
48
|
-
| `--entry <path>`
|
|
49
|
-
| `--include <pattern
|
|
50
|
-
| `--exclude <pattern
|
|
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
51
|
|
|
52
52
|
## Examples
|
|
53
53
|
|
|
@@ -68,12 +68,14 @@ npx lfify --config ./custom-config.json
|
|
|
68
68
|
## Default behavior
|
|
69
69
|
|
|
70
70
|
When no config file is found and no CLI options are provided, lfify uses sensible defaults:
|
|
71
|
+
|
|
71
72
|
- **include**: `**/*` (all files)
|
|
72
73
|
- **exclude**: `node_modules/**`, `.git/**`, `dist/**`, `build/**`, `coverage/**`
|
|
73
74
|
|
|
74
75
|
## Priority
|
|
75
76
|
|
|
76
77
|
CLI options take precedence over config file values:
|
|
78
|
+
|
|
77
79
|
1. CLI arguments (highest)
|
|
78
80
|
2. Config file
|
|
79
81
|
3. Default values (lowest)
|
package/__mocks__/path.js
CHANGED
package/eslint.config.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import globals from
|
|
2
|
-
import pluginJs from
|
|
3
|
-
|
|
1
|
+
import globals from 'globals';
|
|
2
|
+
import pluginJs from '@eslint/js';
|
|
3
|
+
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
4
4
|
|
|
5
5
|
/** @type {import('eslint').Linter.Config[]} */
|
|
6
6
|
export default [
|
|
7
|
-
{ ignores: [
|
|
8
|
-
{ files: [
|
|
7
|
+
{ ignores: ['**/__fixtures__/**'] },
|
|
8
|
+
{ files: ['**/*.js'], languageOptions: { sourceType: 'commonjs' } },
|
|
9
9
|
{
|
|
10
10
|
languageOptions: {
|
|
11
11
|
globals: {
|
|
@@ -15,4 +15,5 @@ export default [
|
|
|
15
15
|
},
|
|
16
16
|
},
|
|
17
17
|
pluginJs.configs.recommended,
|
|
18
|
+
eslintPluginPrettierRecommended,
|
|
18
19
|
];
|
package/index.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { readFile, readdir, rename } = require(
|
|
4
|
-
const { createReadStream, createWriteStream } = require(
|
|
5
|
-
const { resolve, join, relative } = require(
|
|
6
|
-
const { isMatch } = require(
|
|
7
|
-
const { Transform } = require(
|
|
8
|
-
const { pipeline } = require(
|
|
3
|
+
const { readFile, readdir, rename, unlink } = require('fs/promises');
|
|
4
|
+
const { createReadStream, createWriteStream } = require('fs');
|
|
5
|
+
const { resolve, join, relative } = require('path');
|
|
6
|
+
const { isMatch } = require('micromatch');
|
|
7
|
+
const { Transform } = require('stream');
|
|
8
|
+
const { pipeline } = require('stream/promises');
|
|
9
9
|
|
|
10
10
|
/** @type {ReadonlyArray<string>} */
|
|
11
11
|
const LOG_LEVELS = ['error', 'warn', 'info'];
|
|
@@ -43,7 +43,7 @@ const DEFAULT_CONFIG = {
|
|
|
43
43
|
entry: './',
|
|
44
44
|
include: [],
|
|
45
45
|
exclude: [],
|
|
46
|
-
logLevel: 'error'
|
|
46
|
+
logLevel: 'error',
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -53,14 +53,8 @@ const DEFAULT_CONFIG = {
|
|
|
53
53
|
const SENSIBLE_DEFAULTS = {
|
|
54
54
|
entry: './',
|
|
55
55
|
include: ['**/*'],
|
|
56
|
-
exclude: [
|
|
57
|
-
|
|
58
|
-
'.git/**',
|
|
59
|
-
'dist/**',
|
|
60
|
-
'build/**',
|
|
61
|
-
'coverage/**'
|
|
62
|
-
],
|
|
63
|
-
logLevel: 'error'
|
|
56
|
+
exclude: ['node_modules/**', '.git/**', 'dist/**', 'build/**', 'coverage/**'],
|
|
57
|
+
logLevel: 'error',
|
|
64
58
|
};
|
|
65
59
|
|
|
66
60
|
/**
|
|
@@ -69,8 +63,14 @@ const SENSIBLE_DEFAULTS = {
|
|
|
69
63
|
*/
|
|
70
64
|
const CONFIG_SCHEMA = {
|
|
71
65
|
entry: (value) => typeof value === 'string',
|
|
72
|
-
include: (value) =>
|
|
73
|
-
|
|
66
|
+
include: (value) =>
|
|
67
|
+
Array.isArray(value) &&
|
|
68
|
+
value.length > 0 &&
|
|
69
|
+
value.every((item) => typeof item === 'string'),
|
|
70
|
+
exclude: (value) =>
|
|
71
|
+
Array.isArray(value) &&
|
|
72
|
+
value.length > 0 &&
|
|
73
|
+
value.every((item) => typeof item === 'string'),
|
|
74
74
|
logLevel: (value) => LOG_LEVELS.includes(value),
|
|
75
75
|
};
|
|
76
76
|
|
|
@@ -129,13 +129,16 @@ async function readConfig(configPath) {
|
|
|
129
129
|
return {
|
|
130
130
|
...DEFAULT_CONFIG,
|
|
131
131
|
...config,
|
|
132
|
-
entry: resolve(process.cwd(), config.entry || DEFAULT_CONFIG.entry)
|
|
132
|
+
entry: resolve(process.cwd(), config.entry || DEFAULT_CONFIG.entry),
|
|
133
133
|
};
|
|
134
134
|
} catch (err) {
|
|
135
135
|
if (err.code === 'ENOENT') {
|
|
136
136
|
logger.error(`Configuration file not found: ${configPath}`, configPath);
|
|
137
137
|
} else {
|
|
138
|
-
logger.error(
|
|
138
|
+
logger.error(
|
|
139
|
+
`Error reading configuration file: ${err.message}`,
|
|
140
|
+
configPath,
|
|
141
|
+
);
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
if (require.main === module) {
|
|
@@ -168,7 +171,10 @@ async function resolveConfig(cliOptions) {
|
|
|
168
171
|
} catch (err) {
|
|
169
172
|
if (err.code !== 'ENOENT') {
|
|
170
173
|
// Re-throw parsing/validation errors
|
|
171
|
-
logger.error(
|
|
174
|
+
logger.error(
|
|
175
|
+
`Error reading configuration file: ${err.message}`,
|
|
176
|
+
cliOptions.configPath,
|
|
177
|
+
);
|
|
172
178
|
throw err;
|
|
173
179
|
}
|
|
174
180
|
// ENOENT is okay - config file is optional now
|
|
@@ -176,16 +182,29 @@ async function resolveConfig(cliOptions) {
|
|
|
176
182
|
}
|
|
177
183
|
|
|
178
184
|
// Determine final values with precedence: CLI > config file > defaults
|
|
179
|
-
const hasCLIInclude =
|
|
180
|
-
|
|
185
|
+
const hasCLIInclude =
|
|
186
|
+
Array.isArray(cliOptions.include) && cliOptions.include.length > 0;
|
|
187
|
+
const hasCLIExclude =
|
|
188
|
+
Array.isArray(cliOptions.exclude) && cliOptions.exclude.length > 0;
|
|
181
189
|
const hasCLIEntry = typeof cliOptions.entry === 'string';
|
|
182
|
-
const hasCLILogLevel =
|
|
190
|
+
const hasCLILogLevel =
|
|
191
|
+
typeof cliOptions.logLevel === 'string' &&
|
|
192
|
+
LOG_LEVELS.includes(cliOptions.logLevel);
|
|
183
193
|
|
|
184
194
|
const hasFileConfig = fileConfig !== null;
|
|
185
|
-
const hasFileInclude =
|
|
186
|
-
|
|
195
|
+
const hasFileInclude =
|
|
196
|
+
hasFileConfig &&
|
|
197
|
+
Array.isArray(fileConfig.include) &&
|
|
198
|
+
fileConfig.include.length > 0;
|
|
199
|
+
const hasFileExclude =
|
|
200
|
+
hasFileConfig &&
|
|
201
|
+
Array.isArray(fileConfig.exclude) &&
|
|
202
|
+
fileConfig.exclude.length > 0;
|
|
187
203
|
const hasFileEntry = hasFileConfig && typeof fileConfig.entry === 'string';
|
|
188
|
-
const hasFileLogLevel =
|
|
204
|
+
const hasFileLogLevel =
|
|
205
|
+
hasFileConfig &&
|
|
206
|
+
fileConfig.logLevel &&
|
|
207
|
+
LOG_LEVELS.includes(fileConfig.logLevel);
|
|
189
208
|
|
|
190
209
|
// Resolve each config property
|
|
191
210
|
let include, exclude, entry, logLevel;
|
|
@@ -230,7 +249,7 @@ async function resolveConfig(cliOptions) {
|
|
|
230
249
|
entry: resolve(process.cwd(), entry),
|
|
231
250
|
include,
|
|
232
251
|
exclude,
|
|
233
|
-
logLevel
|
|
252
|
+
logLevel,
|
|
234
253
|
};
|
|
235
254
|
}
|
|
236
255
|
|
|
@@ -241,7 +260,7 @@ async function resolveConfig(cliOptions) {
|
|
|
241
260
|
function parseArgs() {
|
|
242
261
|
const args = process.argv.slice(2);
|
|
243
262
|
const options = {
|
|
244
|
-
configPath: '.lfifyrc.json'
|
|
263
|
+
configPath: '.lfifyrc.json',
|
|
245
264
|
};
|
|
246
265
|
|
|
247
266
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -318,18 +337,23 @@ async function convertCRLFtoLF(dirPath, config) {
|
|
|
318
337
|
/**
|
|
319
338
|
* @todo Node.js is single-threaded, if I want to convert files in parallel, I need to use worker_threads
|
|
320
339
|
*/
|
|
321
|
-
await Promise.all(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
340
|
+
await Promise.all(
|
|
341
|
+
entries.map(async (entry) => {
|
|
342
|
+
const fullPath = join(dirPath, entry.name);
|
|
343
|
+
const relativePath = relative(process.cwd(), fullPath).replace(
|
|
344
|
+
/\\/g,
|
|
345
|
+
'/',
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
if (entry.isDirectory()) {
|
|
349
|
+
await convertCRLFtoLF(fullPath, config);
|
|
350
|
+
} else if (entry.isFile() && shouldProcessFile(relativePath, config)) {
|
|
351
|
+
await processFile(fullPath);
|
|
352
|
+
} else {
|
|
353
|
+
logger.info(`skipped: ${relativePath}`, fullPath);
|
|
354
|
+
}
|
|
355
|
+
}),
|
|
356
|
+
);
|
|
333
357
|
} catch (err) {
|
|
334
358
|
logger.error(`error reading directory: ${dirPath}`, dirPath, err);
|
|
335
359
|
throw err;
|
|
@@ -356,13 +380,13 @@ async function processFile(filePath) {
|
|
|
356
380
|
},
|
|
357
381
|
flush(callback) {
|
|
358
382
|
callback(null, this._leftover ?? '');
|
|
359
|
-
}
|
|
383
|
+
},
|
|
360
384
|
});
|
|
361
385
|
try {
|
|
362
386
|
await pipeline(
|
|
363
387
|
createReadStream(filePath, { encoding: 'utf8' }),
|
|
364
388
|
crlf2lf,
|
|
365
|
-
createWriteStream(tmpPath, { encoding: 'utf8' })
|
|
389
|
+
createWriteStream(tmpPath, { encoding: 'utf8' }),
|
|
366
390
|
);
|
|
367
391
|
logger.info(`converted ${filePath}`);
|
|
368
392
|
} catch (err) {
|
|
@@ -373,6 +397,7 @@ async function processFile(filePath) {
|
|
|
373
397
|
await rename(tmpPath, filePath);
|
|
374
398
|
} catch (err) {
|
|
375
399
|
logger.error(`error rename file: ${tmpPath} to ${filePath}`);
|
|
400
|
+
unlink(tmpPath);
|
|
376
401
|
throw err;
|
|
377
402
|
}
|
|
378
403
|
}
|
|
@@ -387,7 +412,7 @@ async function main() {
|
|
|
387
412
|
|
|
388
413
|
await convertCRLFtoLF(config.entry, config);
|
|
389
414
|
|
|
390
|
-
logger.info(
|
|
415
|
+
logger.info('conversion completed.', config.entry);
|
|
391
416
|
}
|
|
392
417
|
|
|
393
418
|
if (require.main === module) {
|
package/index.e2e.test.js
CHANGED
|
@@ -49,7 +49,10 @@ describe('E2E: CRLF to LF with real filesystem', () => {
|
|
|
49
49
|
it('converts files under entry and excludes node_modules and .git', async () => {
|
|
50
50
|
await copyDir(path.join(FIXTURES_DIR, 'default-sensible'), tempDir);
|
|
51
51
|
await fs.rename(path.join(tempDir, '_git'), path.join(tempDir, '.git'));
|
|
52
|
-
await fs.rename(
|
|
52
|
+
await fs.rename(
|
|
53
|
+
path.join(tempDir, '_node_modules'),
|
|
54
|
+
path.join(tempDir, 'node_modules'),
|
|
55
|
+
);
|
|
53
56
|
process.chdir(tempDir);
|
|
54
57
|
|
|
55
58
|
const config = await resolveConfig({});
|
|
@@ -57,14 +60,20 @@ describe('E2E: CRLF to LF with real filesystem', () => {
|
|
|
57
60
|
|
|
58
61
|
const appJs = await fs.readFile(path.join(tempDir, 'src', 'app.js'));
|
|
59
62
|
expect(appJs.includes(CRLF)).toBe(false);
|
|
60
|
-
expect(appJs.equals(Buffer.from('console.log("app");\n', 'utf8'))).toBe(
|
|
63
|
+
expect(appJs.equals(Buffer.from('console.log("app");\n', 'utf8'))).toBe(
|
|
64
|
+
true,
|
|
65
|
+
);
|
|
61
66
|
|
|
62
|
-
const readmeTxt = await fs.readFile(
|
|
67
|
+
const readmeTxt = await fs.readFile(
|
|
68
|
+
path.join(tempDir, 'src', 'readme.txt'),
|
|
69
|
+
);
|
|
63
70
|
expect(readmeTxt.includes(CRLF)).toBe(false);
|
|
64
|
-
expect(readmeTxt.equals(Buffer.from('hello\nworld\n', 'utf8'))).toBe(
|
|
71
|
+
expect(readmeTxt.equals(Buffer.from('hello\nworld\n', 'utf8'))).toBe(
|
|
72
|
+
true,
|
|
73
|
+
);
|
|
65
74
|
|
|
66
75
|
const nodeModulesPkg = await fs.readFile(
|
|
67
|
-
path.join(tempDir, 'node_modules', 'pkg', 'index.js')
|
|
76
|
+
path.join(tempDir, 'node_modules', 'pkg', 'index.js'),
|
|
68
77
|
);
|
|
69
78
|
expect(nodeModulesPkg.includes(CRLF)).toBe(true);
|
|
70
79
|
|
|
@@ -85,7 +94,9 @@ describe('E2E: CRLF to LF with real filesystem', () => {
|
|
|
85
94
|
expect(mainJs.includes(CRLF)).toBe(false);
|
|
86
95
|
expect(mainJs.equals(Buffer.from('const x = 1;\n', 'utf8'))).toBe(true);
|
|
87
96
|
|
|
88
|
-
const skipOtherJs = await fs.readFile(
|
|
97
|
+
const skipOtherJs = await fs.readFile(
|
|
98
|
+
path.join(tempDir, 'skip', 'other.js'),
|
|
99
|
+
);
|
|
89
100
|
expect(skipOtherJs.includes(CRLF)).toBe(true);
|
|
90
101
|
|
|
91
102
|
const docTxt = await fs.readFile(path.join(tempDir, 'doc.txt'));
|
|
@@ -100,7 +111,7 @@ describe('E2E: CRLF to LF with real filesystem', () => {
|
|
|
100
111
|
|
|
101
112
|
const config = await resolveConfig({
|
|
102
113
|
configPath: '.lfifyrc.json',
|
|
103
|
-
include: ['**/*.js']
|
|
114
|
+
include: ['**/*.js'],
|
|
104
115
|
});
|
|
105
116
|
await convertCRLFtoLF(config.entry, config);
|
|
106
117
|
|
package/index.test.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
const mock = require('mock-fs');
|
|
2
|
-
const {
|
|
2
|
+
const {
|
|
3
|
+
readConfig,
|
|
4
|
+
parseArgs,
|
|
5
|
+
processFile,
|
|
6
|
+
resolveConfig,
|
|
7
|
+
shouldProcessFile,
|
|
8
|
+
SENSIBLE_DEFAULTS,
|
|
9
|
+
} = require('./index.cjs');
|
|
3
10
|
const fs = require('fs');
|
|
4
11
|
|
|
5
12
|
function baseMock(overrides = {}) {
|
|
@@ -13,7 +20,7 @@ function baseMock(overrides = {}) {
|
|
|
13
20
|
'node_modules/file.js': 'console.log("test");\r\n',
|
|
14
21
|
'node_modules/subdir/file4.txt': 'test\r\n',
|
|
15
22
|
'index.js': 'console.log("test");\r\n',
|
|
16
|
-
...overrides
|
|
23
|
+
...overrides,
|
|
17
24
|
};
|
|
18
25
|
}
|
|
19
26
|
|
|
@@ -35,17 +42,19 @@ describe('CRLF to LF Converter', () => {
|
|
|
35
42
|
const validConfig = {
|
|
36
43
|
entry: './',
|
|
37
44
|
include: ['*.js'],
|
|
38
|
-
exclude: ['node_modules/**']
|
|
45
|
+
exclude: ['node_modules/**'],
|
|
39
46
|
};
|
|
40
47
|
mock(baseMock({ '.lfifyrc.json': JSON.stringify(validConfig) }));
|
|
41
48
|
|
|
42
49
|
const config = await readConfig('.lfifyrc.json');
|
|
43
50
|
|
|
44
|
-
expect(config).toEqual(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
expect(config).toEqual(
|
|
52
|
+
expect.objectContaining({
|
|
53
|
+
entry: expect.any(String),
|
|
54
|
+
include: expect.any(Array),
|
|
55
|
+
exclude: expect.any(Array),
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
49
58
|
});
|
|
50
59
|
|
|
51
60
|
it('should throw error when config file is not found', async () => {
|
|
@@ -61,7 +70,12 @@ describe('CRLF to LF Converter', () => {
|
|
|
61
70
|
|
|
62
71
|
describe('parseArgs', () => {
|
|
63
72
|
it('should return config path when --config option is provided', () => {
|
|
64
|
-
process.argv = [
|
|
73
|
+
process.argv = [
|
|
74
|
+
'node',
|
|
75
|
+
'lfify',
|
|
76
|
+
'--config',
|
|
77
|
+
'./path/for/test/.lfifyrc.json',
|
|
78
|
+
];
|
|
65
79
|
|
|
66
80
|
const options = parseArgs();
|
|
67
81
|
|
|
@@ -83,7 +97,14 @@ describe('CRLF to LF Converter', () => {
|
|
|
83
97
|
});
|
|
84
98
|
|
|
85
99
|
it('should return multiple include patterns when multiple --include options are provided', () => {
|
|
86
|
-
process.argv = [
|
|
100
|
+
process.argv = [
|
|
101
|
+
'node',
|
|
102
|
+
'lfify',
|
|
103
|
+
'--include',
|
|
104
|
+
'**/*.js',
|
|
105
|
+
'--include',
|
|
106
|
+
'**/*.ts',
|
|
107
|
+
];
|
|
87
108
|
const options = parseArgs();
|
|
88
109
|
expect(options.include).toEqual(['**/*.js', '**/*.ts']);
|
|
89
110
|
});
|
|
@@ -95,7 +116,14 @@ describe('CRLF to LF Converter', () => {
|
|
|
95
116
|
});
|
|
96
117
|
|
|
97
118
|
it('should return multiple exclude patterns when multiple --exclude options are provided', () => {
|
|
98
|
-
process.argv = [
|
|
119
|
+
process.argv = [
|
|
120
|
+
'node',
|
|
121
|
+
'lfify',
|
|
122
|
+
'--exclude',
|
|
123
|
+
'dist/**',
|
|
124
|
+
'--exclude',
|
|
125
|
+
'coverage/**',
|
|
126
|
+
];
|
|
99
127
|
const options = parseArgs();
|
|
100
128
|
expect(options.exclude).toEqual(['dist/**', 'coverage/**']);
|
|
101
129
|
});
|
|
@@ -107,7 +135,20 @@ describe('CRLF to LF Converter', () => {
|
|
|
107
135
|
});
|
|
108
136
|
|
|
109
137
|
it('should handle all options together', () => {
|
|
110
|
-
process.argv = [
|
|
138
|
+
process.argv = [
|
|
139
|
+
'node',
|
|
140
|
+
'lfify',
|
|
141
|
+
'--config',
|
|
142
|
+
'custom.json',
|
|
143
|
+
'--include',
|
|
144
|
+
'*.js',
|
|
145
|
+
'--exclude',
|
|
146
|
+
'dist/**',
|
|
147
|
+
'--entry',
|
|
148
|
+
'./lib',
|
|
149
|
+
'--log-level',
|
|
150
|
+
'info',
|
|
151
|
+
];
|
|
111
152
|
const options = parseArgs();
|
|
112
153
|
expect(options.configPath).toBe('custom.json');
|
|
113
154
|
expect(options.include).toEqual(['*.js']);
|
|
@@ -146,7 +187,9 @@ describe('CRLF to LF Converter', () => {
|
|
|
146
187
|
|
|
147
188
|
it('should return false when file matches exclude pattern', () => {
|
|
148
189
|
const config = { include: ['**/*.js'], exclude: ['node_modules/**'] };
|
|
149
|
-
expect(shouldProcessFile('node_modules/pkg/index.js', config)).toBe(
|
|
190
|
+
expect(shouldProcessFile('node_modules/pkg/index.js', config)).toBe(
|
|
191
|
+
false,
|
|
192
|
+
);
|
|
150
193
|
});
|
|
151
194
|
|
|
152
195
|
it('should return false when file does not match include pattern', () => {
|
|
@@ -155,13 +198,19 @@ describe('CRLF to LF Converter', () => {
|
|
|
155
198
|
});
|
|
156
199
|
|
|
157
200
|
it('should handle multiple include patterns', () => {
|
|
158
|
-
const config = {
|
|
201
|
+
const config = {
|
|
202
|
+
include: ['**/*.js', '**/*.ts'],
|
|
203
|
+
exclude: ['node_modules/**'],
|
|
204
|
+
};
|
|
159
205
|
expect(shouldProcessFile('src/app.js', config)).toBe(true);
|
|
160
206
|
expect(shouldProcessFile('src/app.ts', config)).toBe(true);
|
|
161
207
|
});
|
|
162
208
|
|
|
163
209
|
it('should handle multiple exclude patterns', () => {
|
|
164
|
-
const config = {
|
|
210
|
+
const config = {
|
|
211
|
+
include: ['**/*.js'],
|
|
212
|
+
exclude: ['node_modules/**', 'dist/**', 'build/**', 'coverage/**'],
|
|
213
|
+
};
|
|
165
214
|
expect(shouldProcessFile('src/file.js', config)).toBe(true);
|
|
166
215
|
expect(shouldProcessFile('node_modules/pkg/file.js', config)).toBe(false);
|
|
167
216
|
expect(shouldProcessFile('dist/bundle.js', config)).toBe(false);
|
|
@@ -194,7 +243,7 @@ describe('CRLF to LF Converter', () => {
|
|
|
194
243
|
const options = {
|
|
195
244
|
include: ['**/*.js'],
|
|
196
245
|
exclude: ['node_modules/**'],
|
|
197
|
-
entry: './src'
|
|
246
|
+
entry: './src',
|
|
198
247
|
};
|
|
199
248
|
const config = await resolveConfig(options);
|
|
200
249
|
|
|
@@ -211,17 +260,19 @@ describe('CRLF to LF Converter', () => {
|
|
|
211
260
|
});
|
|
212
261
|
|
|
213
262
|
it('should override config file values with CLI options', async () => {
|
|
214
|
-
mock(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
263
|
+
mock(
|
|
264
|
+
baseMock({
|
|
265
|
+
'.lfifyrc.json': JSON.stringify({
|
|
266
|
+
entry: './',
|
|
267
|
+
include: ['**/*.md'],
|
|
268
|
+
exclude: ['dist/**'],
|
|
269
|
+
}),
|
|
270
|
+
}),
|
|
271
|
+
);
|
|
221
272
|
|
|
222
273
|
const options = {
|
|
223
274
|
configPath: '.lfifyrc.json',
|
|
224
|
-
include: ['**/*.js']
|
|
275
|
+
include: ['**/*.js'],
|
|
225
276
|
};
|
|
226
277
|
const config = await resolveConfig(options);
|
|
227
278
|
|
|
@@ -230,13 +281,15 @@ describe('CRLF to LF Converter', () => {
|
|
|
230
281
|
});
|
|
231
282
|
|
|
232
283
|
it('should load config file when configPath is provided and file exists', async () => {
|
|
233
|
-
mock(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
284
|
+
mock(
|
|
285
|
+
baseMock({
|
|
286
|
+
'.lfifyrc.json': JSON.stringify({
|
|
287
|
+
entry: './lib',
|
|
288
|
+
include: ['**/*.ts'],
|
|
289
|
+
exclude: ['test/**'],
|
|
290
|
+
}),
|
|
291
|
+
}),
|
|
292
|
+
);
|
|
240
293
|
|
|
241
294
|
const options = { configPath: '.lfifyrc.json' };
|
|
242
295
|
const config = await resolveConfig(options);
|
|
@@ -275,30 +328,34 @@ describe('CRLF to LF Converter', () => {
|
|
|
275
328
|
});
|
|
276
329
|
|
|
277
330
|
it('should use logLevel from config file when provided', async () => {
|
|
278
|
-
mock(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
331
|
+
mock(
|
|
332
|
+
baseMock({
|
|
333
|
+
'.lfifyrc.json': JSON.stringify({
|
|
334
|
+
entry: './',
|
|
335
|
+
include: ['**/*.js'],
|
|
336
|
+
exclude: ['node_modules/**'],
|
|
337
|
+
logLevel: 'info',
|
|
338
|
+
}),
|
|
339
|
+
}),
|
|
340
|
+
);
|
|
286
341
|
const config = await resolveConfig({ configPath: '.lfifyrc.json' });
|
|
287
342
|
expect(config.logLevel).toBe('info');
|
|
288
343
|
});
|
|
289
344
|
|
|
290
345
|
it('should override config file logLevel with CLI --log-level', async () => {
|
|
291
|
-
mock(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
346
|
+
mock(
|
|
347
|
+
baseMock({
|
|
348
|
+
'.lfifyrc.json': JSON.stringify({
|
|
349
|
+
entry: './',
|
|
350
|
+
include: ['**/*.js'],
|
|
351
|
+
exclude: ['node_modules/**'],
|
|
352
|
+
logLevel: 'warn',
|
|
353
|
+
}),
|
|
354
|
+
}),
|
|
355
|
+
);
|
|
299
356
|
const config = await resolveConfig({
|
|
300
357
|
configPath: '.lfifyrc.json',
|
|
301
|
-
logLevel: 'info'
|
|
358
|
+
logLevel: 'info',
|
|
302
359
|
});
|
|
303
360
|
expect(config.logLevel).toBe('info');
|
|
304
361
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lfify",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "make your crlf to lf",
|
|
6
6
|
"main": "index.cjs",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"test": "npm run test:unit && npm run test:e2e",
|
|
13
13
|
"test:unit": "jest --testPathPattern=\"index\\.test\\.js\"",
|
|
14
14
|
"test:e2e": "jest --testPathPattern=\"index\\.e2e\\.test\\.js\"",
|
|
15
|
-
"lint": "eslint ."
|
|
15
|
+
"lint": "eslint .",
|
|
16
|
+
"format": "prettier --write .",
|
|
17
|
+
"format:check": "prettier --check ."
|
|
16
18
|
},
|
|
17
19
|
"repository": {
|
|
18
20
|
"type": "git",
|
|
@@ -45,9 +47,12 @@
|
|
|
45
47
|
"@semantic-release/npm": "^13.1.3",
|
|
46
48
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
47
49
|
"eslint": "^9.15.0",
|
|
50
|
+
"eslint-config-prettier": "^10.1.8",
|
|
51
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
48
52
|
"globals": "^15.12.0",
|
|
49
53
|
"jest": "^29.7.0",
|
|
50
54
|
"mock-fs": "^5.5.0",
|
|
55
|
+
"prettier": "^3.8.1",
|
|
51
56
|
"semantic-release": "^25.0.2"
|
|
52
57
|
}
|
|
53
58
|
}
|