Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jonschlinkert committed Aug 5, 2021
1 parent 38b08e0 commit 4245348
Show file tree
Hide file tree
Showing 15 changed files with 369 additions and 46 deletions.
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
*.DS_Store
.idea
.vscode
*.sublime-*

# test related, or directories generated by tests
test/actual
Expand All @@ -18,7 +17,10 @@ npm-debug.log
yarn.lock
yarn-error.log

# misc
# Text editors
*.sublime-*

# custom
_gh_pages
_draft
_drafts
Expand All @@ -27,4 +29,4 @@ vendor
temp
tmp
TODO.md
package-lock.json
package-lock.json
1 change: 0 additions & 1 deletion .npmrc

This file was deleted.

3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ os:
language: node_js
node_js:
- node
- '13'
- '12'
- '11'
- '10'
- '9'
- '8'
Expand Down
54 changes: 52 additions & 2 deletions .verb.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ As of v1.0, parse-gitignore was refactored and simplified down to less than ~50

```js
const fs = require('fs');
const parse = require('{%= name %}');
const gitignore = require('{%= name %}');

// pass the contents of a .gitignore file as a string or buffer
console.log(parse(fs.readFileSync('foo/bar/.gitignore')));
console.log(gitignore(fs.readFileSync('foo/bar/.gitignore')));
//=> ['*.DS_Store', 'node_modules', ...];
```

Expand Down Expand Up @@ -49,3 +49,53 @@ Into an array, like the following:
'*.seed',
'*.pid.lock' ]
```

## .parse

Given the following contents in `foo/bar/.gitignore`:

```
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
```

Results in something like the following:

```js
const parsed = gitignore.parse(fs.readFileSync('foo/bar/.gitignore'));
//
// results in the following
//
{
patterns: [
'logs',
'*.log',
'npm-debug.log*',
'yarn-debug.log*',
'yarn-error.log*',
'pids',
'*.pid',
'*.seed',
'*.pid.lock'
],
sections: [
{
name: 'Logs',
patterns: ['logs', '*.log', 'npm-debug.log*', 'yarn-debug.log*', 'yarn-error.log*']
},
{
name: 'Runtime data',
patterns: ['pids', '*.pid', '*.seed', '*.pid.lock']
}
]
}
```
13 changes: 13 additions & 0 deletions examples/dedupe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

const fs = require('fs');
const path = require('path');
const gitignore = require('..');

const filepath = path.join(__dirname, '../test/fixtures/_gitignore');
const contents = fs.readFileSync(filepath, 'utf8');

// console.log(gitignore.parse(contents));
// console.log(contents.trim() === gitignore.dedupe(contents).trim());
const deduped = gitignore.dedupe(contents);
console.log(deduped);
7 changes: 7 additions & 0 deletions examples/gitignore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const fs = require('fs');
const path = require('path');
const gitignore = require('..');

const filepath = path.join(__dirname, '../test/fixtures/_gitignore');

console.log(gitignore(fs.readFileSync(filepath)));
13 changes: 0 additions & 13 deletions examples/glob-fs.js

This file was deleted.

20 changes: 20 additions & 0 deletions examples/glob.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

const start = Date.now();
process.on('exit', () => console.error(`Total: ${Date.now() - start}ms`));

const fs = require('fs');
const path = require('path');
const glob = require('glob');
const parse = require('..');
const files = [];

const ignore = parse(fs.readFileSync(path.join(__dirname, '../test/fixtures/_gitignore')));

glob('*', { ignore, cwd: __dirname }, (err, files) => {
if (err) {
console.log(err);
} else {
console.log(files);
}
});
7 changes: 5 additions & 2 deletions examples/parse.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const fs = require('fs');
const path = require('path');
const parse = require('..');
console.log(parse(fs.readFileSync(path.join(__dirname, '_gitignore'))));
const gitignore = require('..');

const filepath = path.join(__dirname, '../test/fixtures/_gitignore');

console.log(JSON.stringify(gitignore.parse(fs.readFileSync(filepath)), null, 2));
11 changes: 11 additions & 0 deletions examples/reformat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

const fs = require('fs');
const path = require('path');
const gitignore = require('..');

const filepath = path.join(__dirname, '../.gitignore');
const parsed = gitignore.parse(filepath);

console.log(parsed);
console.log(gitignore.stringify(parsed.sections));
15 changes: 11 additions & 4 deletions examples/stringify.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
const parse = require('..');
const str = fs.readFileSync('.gitignore', 'utf8');
const res = parse(str);
'use strict';

const fs = require('fs');
const path = require('path');
const gitignore = require('..');

const filepath = path.join(__dirname, '../.gitignore');
const contents = fs.readFileSync(filepath);

const res = gitignore.parse(contents);

console.log(res);
console.log(stringify(res.sections));
console.log(gitignore.stringify(res.sections));
147 changes: 130 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,156 @@

'use strict';

const fs = require('fs');

const INVALID_PATH_CHARS_REGEX = /[<>:"|?*]/;
const MAX_PATH_LENGTH = 260;

const isValidPath = input => {
if (typeof input === 'string') {
return input.length <= MAX_PATH_LENGTH && !INVALID_PATH_CHARS_REGEX.test(input);
}
return false;
};

const split = str => String(str).split(/[\r\n]+/);
const isBlank = str => str.trim() === '';
const isComment = str => str.startsWith('#');
const gitignore = input => {
return input.toString()
.split(/\r?\n/)
.filter(l => l.trim() !== '' && l.charAt(0) !== '#');
if (isValidPath(input) && fs.existsSync(input)) return gitignore.file(input);
return split(input).filter(line => !isBlank(line) && !isComment(line));
};

gitignore.parse = (input, fn = line => line) => {
let lines = input.toString().split(/\r?\n/);
let state = { patterns: [], sections: [] };
gitignore.parse = (input, options = {}) => {
if (isValidPath(input) && fs.existsSync(input)) {
return gitignore.parseFile(input);
}

const lines = split(input);
let parsed = { patterns: [], sections: [] };
let section = { name: 'default', patterns: [] };

for (let line of lines) {
for (const line of lines) {
if (line.charAt(0) === '#') {
section = { name: line.slice(1).trim(), patterns: []};
state.sections.push(section);
const [, hash, name] = /^(#+)(.*)$/.exec(line);
section = { name: name.trim(), level: hash.length, patterns: [] };
parsed.sections.push(section);
continue;
}

if (line.trim() !== '') {
let pattern = fn(line, section, state);
section.patterns.push(pattern);
state.patterns.push(pattern);
section.patterns.push(line.trim());
parsed.patterns.push(line.trim());
}
}
return state;

if (options.dedupe !== false) {
parsed = gitignore.dedupe(parsed, { ...options, stringify: false });
}

return parsed;
};

gitignore.format = (section) => {
return `# ${section.name}\n${section.patterns.join('\n')}\n\n`;
gitignore.file = filepath => gitignore(fs.readFileSync(filepath));

gitignore.parseFile = filepath => gitignore.parse(fs.readFileSync(filepath));

gitignore.dedupe = (parsed, options = {}) => {
const seen = { patterns: new Set(), sections: new Map(), all: new Set() };
const sections = [];

if (typeof parsed === 'string') {
parsed = gitignore.parse(parsed);
}

for (let i = 0; i < parsed.sections.length; i++) {
let section = parsed.sections[i];

if (options.onSection) {
section = options.onSection(section);
if (section !== false) sections.push(section);
continue;
}

const existing = seen.sections.get(section.name);

if (existing) {
existing.patterns = [...new Set(existing.patterns.push(section.patterns))];
section = existing;
}

const patterns = [];

for (let pattern of section.patterns) {
const exists = seen.patterns.has(pattern);
seen.patterns.add(pattern);

if (options.onPattern) {
pattern = options.onPattern(pattern, section);
}

if (!exists && pattern !== false) {
patterns.push(pattern);
}
}

seen.sections.set(section.name, section);

if (patterns.length > 0) {
section.patterns = patterns;
sections.push(section);
}
}

parsed.sections = sections;

if (options.stringify === false) {
return parsed;
}

return gitignore.stringify(parsed);
};

/**
* Formats a .gitignore section
*/

gitignore.format = section => {
const heading = section.name ? `# ${section.name}` : '';
return `${heading}\n${section.patterns.join('\n')}\n\n`;
};

gitignore.stringify = (sections, fn = gitignore.format) => {
/**
* Stringify a .gitignore file from parsed sections.
*/

gitignore.stringify = (sections = [], fn = gitignore.format) => {
if (sections.sections) sections = sections.sections;
let result = '';
for (let section of [].concat(sections)) result += fn(section);

for (const section of [].concat(sections)) result += fn(section);
return result.trim();
};

/**
* Re-format a .gitignore file
* @param {String} input File path or contents.
* @param {Object} options
* @return {String} Returns re-formatted contents.
* @api public
*/

gitignore.reformat = (input, options = {}) => {
const parsed = gitignore.parse(input, options);
const output = gitignore.stringify(parsed.sections, options);

if (isValidPath(input) && options.write === true) {
fs.writeFileSync(input, output);
}

return output;
};

/**
* Expose `gitignore`
*/
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@
],
"main": "index.js",
"engines": {
"node": ">=6"
"node": ">=8"
},
"scripts": {
"test": "mocha"
},
"devDependencies": {
"gulp-format-md": "^1.0.0",
"mocha": "^5.2.0"
"gulp-format-md": "^2.0.0",
"mocha": "^8.0.1"
},
"keywords": [
"gitignore",
Expand Down

0 comments on commit 4245348

Please sign in to comment.