1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', { value: true });
|
4 |
|
5 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
6 |
|
7 | var minimist = _interopDefault(require('minimist'));
|
8 | var core = require('@dprint/core');
|
9 | var fs = require('fs');
|
10 | var path = require('path');
|
11 |
|
12 | function parseCommandLineArgs(args) {
|
13 | const argv = minimist(args, {
|
14 | string: ["config"],
|
15 | boolean: ["help", "version", "outputFilePaths", "outputResolvedConfig", "allowNodeModuleFiles", "duration", "init"],
|
16 | });
|
17 | return {
|
18 | allowNodeModuleFiles: argv["allowNodeModuleFiles"],
|
19 | config: getConfigFilePath(),
|
20 | init: argv["init"],
|
21 | showHelp: argv["h"] || argv["help"],
|
22 | showVersion: argv["v"] || argv["version"],
|
23 | outputFilePaths: argv["outputFilePaths"],
|
24 | outputResolvedConfig: argv["outputResolvedConfig"],
|
25 | duration: argv["duration"],
|
26 | filePatterns: argv._,
|
27 | };
|
28 | function getConfigFilePath() {
|
29 | return argv["c"] || argv["config"] || undefined;
|
30 | }
|
31 | }
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | function __awaiter(thisArg, _arguments, P, generator) {
|
49 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
50 | return new (P || (P = Promise))(function (resolve, reject) {
|
51 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
52 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
53 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
54 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
55 | });
|
56 | }
|
57 |
|
58 | const projectTypeInfo = {
|
59 | values: [{
|
60 | name: "openSource",
|
61 | description: "Dprint is formatting an open source project.",
|
62 | }, {
|
63 | name: "commercialSponsored",
|
64 | description: "Dprint is formatting a closed source commercial project and your company sponsored dprint.",
|
65 | }, {
|
66 | name: "commercialDidNotSponsor",
|
67 | description: "Dprint is formatting a closed source commercial project and you want to forever enshrine your name "
|
68 | + "in source control for having specified this.",
|
69 | }],
|
70 | };
|
71 | function getMissingProjectTypeDiagnostic(config) {
|
72 | const validProjectTypes = projectTypeInfo.values.map(v => v.name);
|
73 | if (validProjectTypes.includes(config.projectType || ""))
|
74 | return undefined;
|
75 | const propertyName = "projectType";
|
76 | const largestValueName = validProjectTypes.map(s => s.length).sort().pop();
|
77 | return {
|
78 | propertyName,
|
79 | message: `The "${propertyName}" field is missing. You may specify any of the following possible values in the configuration file according to your `
|
80 | + `conscience and that will supress this warning.\n\n`
|
81 | + projectTypeInfo.values.map(value => ` * ${value.name} ${" ".repeat(largestValueName - value.name.length)}${value.description}`).join("\n"),
|
82 | };
|
83 | }
|
84 |
|
85 | function getPackageVersion() {
|
86 | return "0.11.1";
|
87 | }
|
88 |
|
89 | function getHelpText(plugins) {
|
90 | return `dprint v${getPackageVersion()}
|
91 |
|
92 | Syntax: dprint [options] [...file patterns]
|
93 | Examples: dprint
|
94 | dprint "src/**/*.ts"
|
95 | Options:
|
96 | -h, --help Outputs this message.
|
97 | -v, --version Outputs the version of the library and plugins.
|
98 | --init Creates a dprint.config.js file in the current directory.
|
99 | -c, --config Configuration file to use (default: dprint.config.js)
|
100 | --outputFilePaths Outputs the list of file paths found for formatting without formatting the files.
|
101 | --outputResolvedConfig Outputs the resolved configuration from the configuration file.
|
102 | --duration Outputs how long the format took.
|
103 | --allowNodeModuleFiles Allows including files that have a node_modules directory in their path.
|
104 | ${getPluginTexts()}`;
|
105 | function getPluginTexts() {
|
106 | const prefix = "Plugins:";
|
107 | let result = prefix;
|
108 | if (plugins.length === 0)
|
109 | result += " [No plugins]";
|
110 | else {
|
111 | for (const plugin of plugins)
|
112 | result += `\n* ${plugin.name} v${plugin.version}`;
|
113 | }
|
114 | return result;
|
115 | }
|
116 | }
|
117 |
|
118 | function getVersionText(plugins) {
|
119 | return `dprint v${getPackageVersion()}${getPluginTexts()}`;
|
120 | function getPluginTexts() {
|
121 | let result = "";
|
122 | if (plugins.length === 0)
|
123 | result += " (No plugins)";
|
124 | else {
|
125 | for (const plugin of plugins)
|
126 | result += `\n${plugin.name} v${plugin.version}`;
|
127 | }
|
128 | return result;
|
129 | }
|
130 | }
|
131 |
|
132 | class KillSafeFileWriter {
|
133 | constructor(environment) {
|
134 | this.environment = environment;
|
135 | this.tempFiles = new Set();
|
136 | this.crashed = false;
|
137 | this.crashCleanup = () => {
|
138 | this.crashed = true;
|
139 | for (const filePath of this.tempFiles.values())
|
140 | this.tryDeleteFileSync(filePath);
|
141 | this.tempFiles.clear();
|
142 | };
|
143 | process.on("SIGINT", this.crashCleanup);
|
144 | process.on("SIGUSR1", this.crashCleanup);
|
145 | process.on("SIGUSR2", this.crashCleanup);
|
146 | }
|
147 | dispose() {
|
148 | return __awaiter(this, void 0, void 0, function* () {
|
149 | process.off("SIGINT", this.crashCleanup);
|
150 | process.off("SIGUSR1", this.crashCleanup);
|
151 | process.off("SIGUSR2", this.crashCleanup);
|
152 | const tempFiles = Array.from(this.tempFiles.values());
|
153 | return Promise.all(tempFiles.map(filePath => this.tryDeleteFile(filePath)));
|
154 | });
|
155 | }
|
156 | writeFile(filePath, fileText) {
|
157 | return __awaiter(this, void 0, void 0, function* () {
|
158 | const tempFilePath = this.getTempFileName(filePath);
|
159 | this.tempFiles.add(tempFilePath);
|
160 | yield this.environment.writeFile(tempFilePath, fileText);
|
161 | if (this.crashed) {
|
162 | this.tryDeleteFileSync(tempFilePath);
|
163 | return;
|
164 | }
|
165 | yield this.environment.rename(tempFilePath, filePath);
|
166 | this.tempFiles.delete(tempFilePath);
|
167 | });
|
168 | }
|
169 | tryDeleteFile(filePath) {
|
170 | return __awaiter(this, void 0, void 0, function* () {
|
171 | try {
|
172 | yield this.environment.unlink(filePath);
|
173 | }
|
174 | catch (_a) {
|
175 | }
|
176 | });
|
177 | }
|
178 | tryDeleteFileSync(filePath) {
|
179 | try {
|
180 | this.environment.unlinkSync(filePath);
|
181 | }
|
182 | catch (_a) {
|
183 | }
|
184 | }
|
185 | getTempFileName(filePath) {
|
186 | return filePath + ".dprint_temp";
|
187 | }
|
188 | }
|
189 |
|
190 | function throwError(message) {
|
191 | throw getError(message);
|
192 | }
|
193 | function getError(message) {
|
194 | return new Error(`[dprint]: ${message}`);
|
195 | }
|
196 |
|
197 | function readFile(filePath) {
|
198 | return new Promise((resolve, reject) => {
|
199 | fs.readFile(filePath, { encoding: "utf8" }, (err, text) => {
|
200 | if (err)
|
201 | reject(err);
|
202 | else
|
203 | resolve(text);
|
204 | });
|
205 | });
|
206 | }
|
207 | function writeFile(filePath, text) {
|
208 | return new Promise((resolve, reject) => {
|
209 | fs.writeFile(filePath, text, { encoding: "utf8" }, err => {
|
210 | if (err)
|
211 | reject(err);
|
212 | else
|
213 | resolve();
|
214 | });
|
215 | });
|
216 | }
|
217 | function rename(oldFilePath, newFilePath) {
|
218 | return new Promise((resolve, reject) => {
|
219 | fs.rename(oldFilePath, newFilePath, err => {
|
220 | if (err)
|
221 | reject(err);
|
222 | else
|
223 | resolve();
|
224 | });
|
225 | });
|
226 | }
|
227 | function exists(fileOrDirPath) {
|
228 | return new Promise((resolve, reject) => {
|
229 | try {
|
230 | fs.exists(fileOrDirPath, result => {
|
231 | resolve(result);
|
232 | });
|
233 | }
|
234 | catch (err) {
|
235 | reject(err);
|
236 | }
|
237 | });
|
238 | }
|
239 | function unlink(filePath) {
|
240 | return new Promise((resolve, reject) => {
|
241 | fs.unlink(filePath, err => {
|
242 | if (err)
|
243 | reject(err);
|
244 | else
|
245 | resolve();
|
246 | });
|
247 | });
|
248 | }
|
249 | function unlinkSync(filePath) {
|
250 | fs.unlinkSync(filePath);
|
251 | }
|
252 |
|
253 | function resolveConfigFile(filePath, environment) {
|
254 | return __awaiter(this, void 0, void 0, function* () {
|
255 | const resolvedFilePath = resolveConfigFilePath(filePath, environment);
|
256 | return {
|
257 | filePath: resolvedFilePath,
|
258 | config: yield getConfig(),
|
259 | };
|
260 | function getConfig() {
|
261 | var _a, _b;
|
262 | return __awaiter(this, void 0, void 0, function* () {
|
263 | let config;
|
264 | try {
|
265 | try {
|
266 | config = yield environment.require(resolvedFilePath);
|
267 | }
|
268 | catch (err) {
|
269 | if (yield environment.exists(resolvedFilePath))
|
270 | return throwError(`Error loading configuration file '${resolvedFilePath}'.\n\n${err}`);
|
271 | else if (filePath == null) {
|
272 | return throwError(`Could not find configuration file at '${resolvedFilePath}'. `
|
273 | + `Did you mean to create one (dprint --init) or specify a --config option?\n\n`
|
274 | + err);
|
275 | }
|
276 | else {
|
277 | return throwError(`Could not find specified configuration file at '${resolvedFilePath}'. Did you mean to create it?\n\n` + err);
|
278 | }
|
279 | }
|
280 | if (typeof config !== "object" || typeof config.config !== "object")
|
281 | return throwError(`Expected an object to be exported on the 'config' named export of the configuration at '${resolvedFilePath}'.`);
|
282 | else
|
283 | return config.config;
|
284 | }
|
285 | catch (err) {
|
286 | const plugins = (_b = (_a = config) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.plugins;
|
287 | if (plugins instanceof Array)
|
288 | plugins.forEach(p => { var _a, _b; return (_b = (_a = p) === null || _a === void 0 ? void 0 : _a.dispose) === null || _b === void 0 ? void 0 : _b.call(_a); });
|
289 | throw err;
|
290 | }
|
291 | });
|
292 | }
|
293 | });
|
294 | }
|
295 | function resolveConfigFilePath(filePath, environment) {
|
296 | return environment.resolvePath(filePath || "dprint.config.js");
|
297 | }
|
298 |
|
299 | function runCli(args, environment) {
|
300 | return __awaiter(this, void 0, void 0, function* () {
|
301 | const options = parseCommandLineArgs(args);
|
302 | yield runCliWithOptions(options, environment);
|
303 | });
|
304 | }
|
305 | function runCliWithOptions(options, environment) {
|
306 | return __awaiter(this, void 0, void 0, function* () {
|
307 | const startDate = new Date();
|
308 | if (options.showHelp) {
|
309 | environment.log(getHelpText(yield safeGetPlugins()));
|
310 | return;
|
311 | }
|
312 | else if (options.showVersion) {
|
313 | environment.log(getVersionText(yield safeGetPlugins()));
|
314 | return;
|
315 | }
|
316 | else if (options.init) {
|
317 | yield createConfigFile(environment);
|
318 | return;
|
319 | }
|
320 | const { unresolvedConfiguration, configFilePath, plugins } = yield getUnresolvedConfigAndPlugins();
|
321 | try {
|
322 | yield runCliWithPlugins();
|
323 | }
|
324 | finally {
|
325 | plugins.forEach(p => { var _a, _b; return (_b = (_a = p).dispose) === null || _b === void 0 ? void 0 : _b.call(_a); });
|
326 | }
|
327 | if (options.duration) {
|
328 | const durationInSeconds = ((new Date()).getTime() - startDate.getTime()) / 1000;
|
329 | environment.log(`Duration: ${durationInSeconds.toFixed(2)}s`);
|
330 | }
|
331 | function runCliWithPlugins() {
|
332 | return __awaiter(this, void 0, void 0, function* () {
|
333 | const globalConfig = resolveGlobalConfigurationInternal();
|
334 | updatePluginsWithConfiguration();
|
335 | const filePaths = yield getFilePaths();
|
336 | if (options.outputResolvedConfig) {
|
337 | outputResolvedConfiguration();
|
338 | return;
|
339 | }
|
340 | else if (options.outputFilePaths) {
|
341 | if (filePaths.length > 0) {
|
342 | for (const filePath of filePaths)
|
343 | environment.log(filePath);
|
344 | }
|
345 | else {
|
346 | environment.log("Found 0 files.");
|
347 | }
|
348 | return;
|
349 | }
|
350 | const promises = [];
|
351 | const killSafeFileWriter = new KillSafeFileWriter(environment);
|
352 | try {
|
353 | for (const filePath of filePaths) {
|
354 | const promise = environment.readFile(filePath).then(fileText => {
|
355 | const result = core.formatFileText({
|
356 | filePath,
|
357 | fileText,
|
358 | plugins,
|
359 | });
|
360 | return result === fileText ? Promise.resolve() : killSafeFileWriter.writeFile(filePath, result);
|
361 | }).catch(err => {
|
362 | const errorText = err.toString().replace("[dprint]: ", "");
|
363 | environment.error(`Error formatting file: ${filePath}\n\n${errorText}`);
|
364 | });
|
365 | promises.push(promise);
|
366 | }
|
367 | yield Promise.all(promises);
|
368 | }
|
369 | finally {
|
370 | yield killSafeFileWriter.dispose();
|
371 | }
|
372 | function updatePluginsWithConfiguration() {
|
373 | for (const plugin of plugins) {
|
374 | plugin.initialize({
|
375 | environment,
|
376 | globalConfig,
|
377 | });
|
378 | for (const diagnostic of plugin.getConfigurationDiagnostics())
|
379 | warnForConfigurationDiagnostic(diagnostic);
|
380 | }
|
381 | }
|
382 | function outputResolvedConfiguration() {
|
383 | environment.log(getText());
|
384 | function getText() {
|
385 | let text = `Global configuration: ${prettyPrintAsJson(globalConfig)}`;
|
386 | for (const plugin of plugins)
|
387 | text += `\n${plugin.name}: ${prettyPrintAsJson(plugin.getConfiguration())}`;
|
388 | return text;
|
389 | }
|
390 | function prettyPrintAsJson(obj) {
|
391 | const numSpaces = 2;
|
392 | return JSON.stringify(obj, null, numSpaces);
|
393 | }
|
394 | }
|
395 | });
|
396 | }
|
397 | function resolveGlobalConfigurationInternal() {
|
398 | const missingProjectTypeDiagnostic = getMissingProjectTypeDiagnostic(unresolvedConfiguration);
|
399 | const configResult = core.resolveConfiguration(getUnresolvedConfigStrippedOfCliSpecificConfig());
|
400 | for (const diagnostic of configResult.diagnostics)
|
401 | warnForConfigurationDiagnostic(diagnostic);
|
402 | if (missingProjectTypeDiagnostic)
|
403 | warnForConfigurationDiagnostic(missingProjectTypeDiagnostic);
|
404 | return configResult.config;
|
405 | function getUnresolvedConfigStrippedOfCliSpecificConfig() {
|
406 | const obj = Object.assign({}, unresolvedConfiguration);
|
407 | delete obj.excludes;
|
408 | delete obj.includes;
|
409 | return obj;
|
410 | }
|
411 | }
|
412 | function getFilePaths() {
|
413 | return __awaiter(this, void 0, void 0, function* () {
|
414 | const isInNodeModules = /[\/|\\]node_modules[\/|\\]/i;
|
415 | const allFilePaths = yield environment.glob(getFileGlobs());
|
416 | return options.allowNodeModuleFiles ? allFilePaths : allFilePaths.filter(filePath => !isInNodeModules.test(filePath));
|
417 | function getFileGlobs() {
|
418 | return [...getIncludes(), ...getExcludes()];
|
419 | function getIncludes() {
|
420 | if (options.filePatterns.length > 0) {
|
421 | if (!options.outputFilePaths && unresolvedConfiguration.includes && unresolvedConfiguration.includes.length > 0)
|
422 | environment.warn("Ignoring the configuration file's includes because file patterns were provided to the command line.");
|
423 | return options.filePatterns;
|
424 | }
|
425 | return unresolvedConfiguration.includes || [];
|
426 | }
|
427 | function getExcludes() {
|
428 | if (!unresolvedConfiguration.excludes)
|
429 | return [];
|
430 | return unresolvedConfiguration.excludes.map(pattern => {
|
431 | if (pattern.startsWith("!"))
|
432 | return pattern;
|
433 | return "!" + pattern;
|
434 | });
|
435 | }
|
436 | }
|
437 | });
|
438 | }
|
439 | function warnForConfigurationDiagnostic(diagnostic) {
|
440 | environment.warn(`[${environment.basename(configFilePath)}]: ${diagnostic.message}`);
|
441 | }
|
442 | function safeGetPlugins() {
|
443 | return __awaiter(this, void 0, void 0, function* () {
|
444 | try {
|
445 | return (yield getUnresolvedConfigAndPlugins()).plugins;
|
446 | }
|
447 | catch (err) {
|
448 | return [];
|
449 | }
|
450 | });
|
451 | }
|
452 | function getUnresolvedConfigAndPlugins() {
|
453 | return __awaiter(this, void 0, void 0, function* () {
|
454 | const { config: unresolvedConfiguration, filePath: configFilePath } = yield resolveConfigFile(options.config, environment);
|
455 | return {
|
456 | unresolvedConfiguration,
|
457 | configFilePath,
|
458 | plugins: unresolvedConfiguration.plugins || [],
|
459 | };
|
460 | });
|
461 | }
|
462 | });
|
463 | }
|
464 | function createConfigFile(environment) {
|
465 | return __awaiter(this, void 0, void 0, function* () {
|
466 | const filePath = resolveConfigFilePath(undefined, environment);
|
467 | if (yield environment.exists(filePath)) {
|
468 | environment.warn(`Skipping initialization because a configuration file already exists at: ${filePath}`);
|
469 | return;
|
470 | }
|
471 | environment.writeFile(filePath, getDefaultConfigFileText());
|
472 | environment.log(`Created ${filePath}`);
|
473 | function getDefaultConfigFileText() {
|
474 | return `// @ts-check
|
475 | const { TypeScriptPlugin } = require("dprint-plugin-typescript");
|
476 | const { JsoncPlugin } = require("dprint-plugin-jsonc");
|
477 |
|
478 | /** @type { import("dprint").Configuration } */
|
479 | module.exports.config = {
|
480 | projectType: "openSource",
|
481 | plugins: [
|
482 | new TypeScriptPlugin({
|
483 | }),
|
484 | new JsoncPlugin({
|
485 | indentWidth: 2,
|
486 | }),
|
487 | ],
|
488 | includes: [
|
489 | "**/*.{ts,tsx,json,js,jsx}",
|
490 | ],
|
491 | };
|
492 | `;
|
493 | }
|
494 | });
|
495 | }
|
496 |
|
497 | class CliEnvironment extends core.CliLoggingEnvironment {
|
498 | constructor() {
|
499 | super(...arguments);
|
500 | this.fastGlob = require("fast-glob");
|
501 | }
|
502 | basename(fileOrDirPath) {
|
503 | return path.basename(fileOrDirPath);
|
504 | }
|
505 | resolvePath(fileOrDirPath) {
|
506 | return path.normalize(path.resolve(fileOrDirPath));
|
507 | }
|
508 | readFile(filePath) {
|
509 | return readFile(filePath);
|
510 | }
|
511 | writeFile(filePath, text) {
|
512 | return writeFile(filePath, text);
|
513 | }
|
514 | exists(filePath) {
|
515 | return exists(filePath);
|
516 | }
|
517 | glob(patterns) {
|
518 | return this.fastGlob(backSlashesToForward(patterns), {
|
519 | absolute: true,
|
520 | cwd: path.resolve("."),
|
521 | });
|
522 | }
|
523 | require(filePath) {
|
524 | return new Promise((resolve, reject) => {
|
525 | try {
|
526 | resolve(require(filePath));
|
527 | }
|
528 | catch (err) {
|
529 | reject(err);
|
530 | }
|
531 | });
|
532 | }
|
533 | rename(oldFilePath, newFilePath) {
|
534 | return rename(oldFilePath, newFilePath);
|
535 | }
|
536 | unlink(filePath) {
|
537 | return unlink(filePath);
|
538 | }
|
539 | unlinkSync(filePath) {
|
540 | unlinkSync(filePath);
|
541 | }
|
542 | }
|
543 | function backSlashesToForward(patterns) {
|
544 | return patterns.map(p => p.replace(/\\/g, "/"));
|
545 | }
|
546 |
|
547 | exports.CliEnvironment = CliEnvironment;
|
548 | exports.runCli = runCli;
|