1 | const path = require('path')
|
2 | const fs = require('fs')
|
3 | const parseIgnore = require('parse-gitignore')
|
4 | const { promisify } = require('util')
|
5 | const readFile = promisify(fs.readFile)
|
6 | const writeFile = promisify(fs.writeFile)
|
7 |
|
8 | function fileExists(filePath) {
|
9 | return new Promise((resolve, reject) => {
|
10 | fs.access(filePath, fs.F_OK, err => {
|
11 | if (err) return resolve(false)
|
12 | return resolve(true)
|
13 | })
|
14 | })
|
15 | }
|
16 |
|
17 | async function hasGitIgnore(dir) {
|
18 | const gitIgnorePath = path.join(dir, '.gitignore')
|
19 | const hasIgnore = await fileExists(gitIgnorePath)
|
20 | return hasIgnore
|
21 | }
|
22 |
|
23 | function parser(input, fn = line => line) {
|
24 | let lines = input.toString().split(/\r?\n/)
|
25 | let section = { name: 'default', patterns: [] }
|
26 | let state = { patterns: [], sections: [section] }
|
27 |
|
28 | for (let line of lines) {
|
29 | if (line.charAt(0) === '#') {
|
30 | section = { name: line.slice(1).trim(), patterns: [] }
|
31 | state.sections.push(section)
|
32 | continue
|
33 | }
|
34 |
|
35 | if (line.trim() !== '') {
|
36 | let pattern = fn(line, section, state)
|
37 | section.patterns.push(pattern)
|
38 | state.patterns.push(pattern)
|
39 | }
|
40 | }
|
41 | return state
|
42 | }
|
43 |
|
44 | function stringify(state) {
|
45 | return parseIgnore.stringify(state.sections, section => {
|
46 | if (!section.patterns.length) {
|
47 | return ''
|
48 | }
|
49 |
|
50 | return `# ${section.name}\n${section.patterns.join('\n')}\n\n`
|
51 | })
|
52 | }
|
53 |
|
54 | function parse(input, fn) {
|
55 | const state = parser(input, fn)
|
56 |
|
57 | state.concat = i => {
|
58 | const newState = parser(i, fn)
|
59 |
|
60 | for (let s2 in newState.sections) {
|
61 | const sec2 = newState.sections[s2]
|
62 |
|
63 | let sectionExists = false
|
64 | for (let s1 in state.sections) {
|
65 | const sec1 = state.sections[s1]
|
66 |
|
67 |
|
68 | if (sec1.name === sec2.name) {
|
69 | sectionExists = true
|
70 | sec1.patterns = Array.from(new Set(sec1.patterns.concat(sec2.patterns)))
|
71 | }
|
72 | }
|
73 |
|
74 |
|
75 | if (!sectionExists) {
|
76 | state.sections.push(sec2)
|
77 | }
|
78 | }
|
79 |
|
80 | return state
|
81 | }
|
82 |
|
83 | return state
|
84 | }
|
85 |
|
86 | async function ensureNetlifyIgnore(dir) {
|
87 | const gitIgnorePath = path.join(dir, '.gitignore')
|
88 | const ignoreContent = '# Local Netlify folder\n.netlify'
|
89 |
|
90 |
|
91 | if (!(await hasGitIgnore(dir))) {
|
92 | await writeFile(gitIgnorePath, ignoreContent, 'utf8')
|
93 | return false
|
94 | }
|
95 |
|
96 | let gitIgnoreContents
|
97 | let ignorePatterns
|
98 | try {
|
99 | gitIgnoreContents = await readFile(gitIgnorePath, 'utf8')
|
100 | ignorePatterns = parseIgnore.parse(gitIgnoreContents)
|
101 | } catch (e) {
|
102 |
|
103 | }
|
104 |
|
105 | if (!ignorePatterns || !ignorePatterns.patterns.includes('.netlify')) {
|
106 | const newContents = `${gitIgnoreContents}\n${ignoreContent}`
|
107 | await writeFile(gitIgnorePath, newContents, 'utf8')
|
108 | }
|
109 | }
|
110 |
|
111 | module.exports = {
|
112 | parse: parse,
|
113 | stringify: stringify,
|
114 | format: parseIgnore.format,
|
115 | ensureNetlifyIgnore
|
116 | }
|