Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(config): create a config file on the first run #15

Merged
merged 7 commits into from
Feb 18, 2023
15 changes: 12 additions & 3 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ import { red } from 'colorette';
import { program } from 'commander';
import type { Issue } from 'cspell';
import { lint } from 'cspell';
import { findConfig } from './config';
import { reportErrors, reportSuccess, resetDisplay, showProgress, showStartupMessage, stopSpinner } from './display';
import { findOrCreateConfig } from './config';
import {
reportErrors,
reportSuccess,
resetDisplay,
showConfigurationFilePath,
showProgress,
showStartupMessage,
stopSpinner
} from './display';
import { handleIssues } from './handleIssue';

interface CLIOptions {
Expand Down Expand Up @@ -52,7 +60,8 @@ const globs = program.processedArgs[0];
showStartupMessage(globs);

const start = async () => {
const configPath = await findConfig(options.config);
const configPath = await findOrCreateConfig(options.config);
showConfigurationFilePath(configPath);

const {
issues: issueCount,
Expand Down
21 changes: 15 additions & 6 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { extname } from 'node:path';
import { existsSync } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';
import type { CSpellSettings } from 'cspell';
import { searchForConfig } from 'cspell-lib';
Expand All @@ -7,19 +8,31 @@ import { previousState } from './shared';

let configPath: string | undefined;

export const findConfig = async (config?: string) => {
export const writeToSettings = async (settings: CSpellSettings | { cspell: CSpellSettings }) => {
await writeFile(configPath!, JSON.stringify(settings, null, 4));
};

export const findOrCreateConfig = async (config?: string) => {
// Try to locate a config file in the current working directory, or with the `config` option if it was provided.
const configSource = config ?? (await searchForConfig(process.cwd()))?.__importRef?.filename;

// LocalConfigPath is a path to a config file in the user's configuration directory (platform dependent).
const localConfigPath = (await import('application-config-path')).default('cspell.json');
guilherssousa marked this conversation as resolved.
Show resolved Hide resolved

// If no config file was found, use/create a config file in the user's configuration directory (platform dependent).
const path = configSource ?? (await import('application-config-path')).default('cspell.json');
guilherssousa marked this conversation as resolved.
Show resolved Hide resolved
const path = configSource ?? localConfigPath;

// Only JSON files are supported to prevent more dependencies for yml parsing. If the config file is not a JSON
// file, it can still be used, but it won't be updated with new ignored words.
if (extname(path) === '.json') {
configPath = path;
}

// If the path is the local config path and it doesn't exist, create it.
if (path === localConfigPath && !existsSync(localConfigPath)) {
await writeToSettings({});
}

return path;
};

Expand All @@ -37,10 +50,6 @@ export const getSettings = async (): Promise<CSpellSettings | { cspell: CSpellSe
}
};

export const writeToSettings = async (settings: CSpellSettings | { cspell: CSpellSettings }) => {
await writeFile(configPath!, JSON.stringify(settings, null, 4));
};

export const addIgnoreWordToSettings = async (word: string) => {
const settings = await getSettings();
if (!settings) {
Expand Down
4 changes: 4 additions & 0 deletions src/display.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ export const showStartupMessage = (globs: string[]) => {
console.log(`\nFinding files matching ${cyan(globs.join(', '))}`);
};

export const showConfigurationFilePath = (path: string) => {
console.log(`Using configuration from ${cyan(path)}\n`);
guilherssousa marked this conversation as resolved.
Show resolved Hide resolved
};

let spinner: Spinner | undefined;
export const stopSpinner = () => {
spinner?.stop();
Expand Down
21 changes: 21 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import fs from 'node:fs';
import { join } from 'node:path';
import { writeFile } from 'node:fs/promises';
import { vi, afterEach, describe, test, expect } from 'vitest';
import { handleIssues } from '../src/handleIssue';
import { determineAction, formatContext } from '../src/display';
import { findOrCreateConfig } from '../src/config';
import { Action } from '../src/shared';
import { allTypoSets } from './fixtures/data';
import { sampleReplacer } from './mocks/issue';
Expand Down Expand Up @@ -55,3 +58,21 @@ describe.each(allTypoSets)('%s', (name, data) => {
expect(contextDisplays, name).toMatchObject(data.displays);
});
});

describe('findConfig', () => {
test('should find a mock config file in working directory', async () => {
const readFileSpy = vi.spyOn<typeof fs, 'readFile'>(fs, 'readFile');
guilherssousa marked this conversation as resolved.
Show resolved Hide resolved

readFileSpy.mockImplementation(((path: string, _encoding: any, callback: (err: null, data: string) => void) => {
if (path === join(process.cwd(), 'cspell.json')) {
guilherssousa marked this conversation as resolved.
Show resolved Hide resolved
callback(null, '{}');
} else {
callback(null, '');
}
guilherssousa marked this conversation as resolved.
Show resolved Hide resolved
}) as typeof fs.readFile);

const config = await findOrCreateConfig();

expect(config).toBe(join(process.cwd(), 'cspell.json'));
});
});