UNPKG

4.97 kBJavaScriptView Raw
1"use strict";
2
3const fs = require("./utils/fs");
4const modeUtil = require("./utils/mode");
5const validate = require("./utils/validate");
6const write = require("./write");
7
8const validateInput = (methodName, path, criteria) => {
9 const methodSignature = `${methodName}(path, [criteria])`;
10 validate.argument(methodSignature, "path", path, ["string"]);
11 validate.options(methodSignature, "criteria", criteria, {
12 content: ["string", "buffer", "object", "array"],
13 jsonIndent: ["number"],
14 mode: ["string", "number"]
15 });
16};
17
18const getCriteriaDefaults = passedCriteria => {
19 const criteria = passedCriteria || {};
20 if (criteria.mode !== undefined) {
21 criteria.mode = modeUtil.normalizeFileMode(criteria.mode);
22 }
23 return criteria;
24};
25
26const generatePathOccupiedByNotFileError = path => {
27 return new Error(
28 `Path ${path} exists but is not a file. Halting jetpack.file() call for safety reasons.`
29 );
30};
31
32// ---------------------------------------------------------
33// Sync
34// ---------------------------------------------------------
35
36const checkWhatAlreadyOccupiesPathSync = path => {
37 let stat;
38
39 try {
40 stat = fs.statSync(path);
41 } catch (err) {
42 // Detection if path exists
43 if (err.code !== "ENOENT") {
44 throw err;
45 }
46 }
47
48 if (stat && !stat.isFile()) {
49 throw generatePathOccupiedByNotFileError(path);
50 }
51
52 return stat;
53};
54
55const checkExistingFileFulfillsCriteriaSync = (path, stat, criteria) => {
56 const mode = modeUtil.normalizeFileMode(stat.mode);
57
58 const checkContent = () => {
59 if (criteria.content !== undefined) {
60 write.sync(path, criteria.content, {
61 mode,
62 jsonIndent: criteria.jsonIndent
63 });
64 return true;
65 }
66 return false;
67 };
68
69 const checkMode = () => {
70 if (criteria.mode !== undefined && criteria.mode !== mode) {
71 fs.chmodSync(path, criteria.mode);
72 }
73 };
74
75 const contentReplaced = checkContent();
76 if (!contentReplaced) {
77 checkMode();
78 }
79};
80
81const createBrandNewFileSync = (path, criteria) => {
82 let content = "";
83 if (criteria.content !== undefined) {
84 content = criteria.content;
85 }
86 write.sync(path, content, {
87 mode: criteria.mode,
88 jsonIndent: criteria.jsonIndent
89 });
90};
91
92const fileSync = (path, passedCriteria) => {
93 const criteria = getCriteriaDefaults(passedCriteria);
94 const stat = checkWhatAlreadyOccupiesPathSync(path);
95 if (stat !== undefined) {
96 checkExistingFileFulfillsCriteriaSync(path, stat, criteria);
97 } else {
98 createBrandNewFileSync(path, criteria);
99 }
100};
101
102// ---------------------------------------------------------
103// Async
104// ---------------------------------------------------------
105
106const checkWhatAlreadyOccupiesPathAsync = path => {
107 return new Promise((resolve, reject) => {
108 fs.stat(path)
109 .then(stat => {
110 if (stat.isFile()) {
111 resolve(stat);
112 } else {
113 reject(generatePathOccupiedByNotFileError(path));
114 }
115 })
116 .catch(err => {
117 if (err.code === "ENOENT") {
118 // Path doesn't exist.
119 resolve(undefined);
120 } else {
121 // This is other error. Must end here.
122 reject(err);
123 }
124 });
125 });
126};
127
128const checkExistingFileFulfillsCriteriaAsync = (path, stat, criteria) => {
129 const mode = modeUtil.normalizeFileMode(stat.mode);
130
131 const checkContent = () => {
132 return new Promise((resolve, reject) => {
133 if (criteria.content !== undefined) {
134 write
135 .async(path, criteria.content, {
136 mode,
137 jsonIndent: criteria.jsonIndent
138 })
139 .then(() => {
140 resolve(true);
141 })
142 .catch(reject);
143 } else {
144 resolve(false);
145 }
146 });
147 };
148
149 const checkMode = () => {
150 if (criteria.mode !== undefined && criteria.mode !== mode) {
151 return fs.chmod(path, criteria.mode);
152 }
153 return undefined;
154 };
155
156 return checkContent().then(contentReplaced => {
157 if (!contentReplaced) {
158 return checkMode();
159 }
160 return undefined;
161 });
162};
163
164const createBrandNewFileAsync = (path, criteria) => {
165 let content = "";
166 if (criteria.content !== undefined) {
167 content = criteria.content;
168 }
169
170 return write.async(path, content, {
171 mode: criteria.mode,
172 jsonIndent: criteria.jsonIndent
173 });
174};
175
176const fileAsync = (path, passedCriteria) => {
177 return new Promise((resolve, reject) => {
178 const criteria = getCriteriaDefaults(passedCriteria);
179
180 checkWhatAlreadyOccupiesPathAsync(path)
181 .then(stat => {
182 if (stat !== undefined) {
183 return checkExistingFileFulfillsCriteriaAsync(path, stat, criteria);
184 }
185 return createBrandNewFileAsync(path, criteria);
186 })
187 .then(resolve, reject);
188 });
189};
190
191// ---------------------------------------------------------
192// API
193// ---------------------------------------------------------
194
195exports.validateInput = validateInput;
196exports.sync = fileSync;
197exports.async = fileAsync;