Skip to content

Commit

Permalink
feat(config): diversification of configuration
Browse files Browse the repository at this point in the history
- enhance configuration reading
  - configuration can be read from the `.ctirc`, `tsconfig.json`, `package.json`
- enhance initialization command
  - initialization command can select the position: `.ctirc`, `tsconfig.json`, `package.json`
  • Loading branch information
imjuni committed Aug 1, 2024
1 parent 8dd84a9 commit 5962b76
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 29 deletions.
1 change: 1 addition & 0 deletions src/cli/interfaces/IInitQuestionAnswer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export interface IInitQuestionAnswer {
tsconfig: string[];
mode: CE_CTIX_BUILD_MODE;
overwirte: boolean;
configPosition: '.ctirc' | 'tsconfig.json' | 'package.json';
exportFilename: string;
}
54 changes: 31 additions & 23 deletions src/cli/questions/askInitOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,7 @@ export async function askInitOptions(): Promise<IInitQuestionAnswer> {
const tsconfigFiles = getGlobFiles(glob);
const sortedTsconfigFiles = tsconfigFiles.sort(getTsconfigComparer(cwdAnswer.cwd));

if (optionFileExist) {
const overwriteAnswer = await inquirer.prompt<Pick<IInitQuestionAnswer, 'overwirte'>>([
{
type: 'confirm',
name: 'overwirte',
message: `Already exists ${chalk.redBright(optionFilePath)}, overwrite it?`,
default: false,
},
]);

if (!overwriteAnswer.overwirte) {
return {
cwd: cwdAnswer.cwd,
overwirte: overwriteAnswer.overwirte,
tsconfig: [],
mode: CE_CTIX_BUILD_MODE.BUNDLE_MODE,
exportFilename: CE_CTIX_DEFAULT_VALUE.EXPORT_FILENAME,
} satisfies IInitQuestionAnswer;
}
}

const selectedConfig = await inquirer.prompt<Omit<IInitQuestionAnswer, 'cwd' | 'overwrite'>>([
const userSelectedAnswer = await inquirer.prompt<Omit<IInitQuestionAnswer, 'cwd' | 'overwrite'>>([
{
type: 'checkbox',
name: 'tsconfig',
Expand All @@ -67,6 +46,13 @@ export async function askInitOptions(): Promise<IInitQuestionAnswer> {
default: CE_CTIX_BUILD_MODE.BUNDLE_MODE,
choices: [CE_CTIX_BUILD_MODE.BUNDLE_MODE, CE_CTIX_BUILD_MODE.CREATE_MODE],
},
{
type: 'list',
name: 'configPosition',
message: 'Where do you want to add the configuration?',
default: '.ctirc',
choices: ['.ctirc', 'tsconfig.json', 'package.json'],
},
{
type: 'input',
name: 'exportFilename',
Expand All @@ -75,9 +61,31 @@ export async function askInitOptions(): Promise<IInitQuestionAnswer> {
},
]);

if (userSelectedAnswer.configPosition === '.ctirc' && optionFileExist) {
const overwriteAnswer = await inquirer.prompt<Pick<IInitQuestionAnswer, 'overwirte'>>([
{
type: 'confirm',
name: 'overwirte',
message: `Already exists ${chalk.redBright(optionFilePath)}, overwrite it?`,
default: false,
},
]);

if (!overwriteAnswer.overwirte) {
return {
cwd: cwdAnswer.cwd,
overwirte: overwriteAnswer.overwirte,
tsconfig: [],
mode: CE_CTIX_BUILD_MODE.BUNDLE_MODE,
configPosition: '.ctirc',
exportFilename: CE_CTIX_DEFAULT_VALUE.EXPORT_FILENAME,
} satisfies IInitQuestionAnswer;
}
}

const answer: IInitQuestionAnswer = {
...cwdAnswer,
...selectedConfig,
...userSelectedAnswer,
overwirte: true,
};

Expand Down
76 changes: 73 additions & 3 deletions src/configs/loadConfig.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { Spinner } from '#/cli/ux/Spinner';
import { castConfig } from '#/configs/castConfig';
import { CE_CTIX_DEFAULT_VALUE } from '#/configs/const-enum/CE_CTIX_DEFAULT_VALUE';
import { getConfigValue } from '#/configs/getConfigValue';
import type { IProjectOptions } from '#/configs/interfaces/IProjectOptions';
import type { TCommandBuildArgvOptions } from '#/configs/interfaces/TCommandBuildArgvOptions';
import type { TCommandRemoveOptions } from '#/configs/interfaces/TCommandRemoveOptions';
import { getCommand } from '#/configs/modules/getCommand';
import { readJsonConfig } from '#/configs/modules/json/readJsonConfig';
import { parseConfig } from '#/configs/parseConfig';
import { getConfigFilePath } from '#/modules/path/getConfigFilePath';
import { posixJoin } from '#/modules/path/modules/posixJoin';
import consola from 'consola';
import findUp from 'find-up';
import minimist from 'minimist';
import { isError } from 'my-easy-fp';
import fs from 'node:fs';
import type { PackageJson, TsConfigJson } from 'type-fest';

export async function loadConfig(): Promise<
TCommandBuildArgvOptions | TCommandRemoveOptions | IProjectOptions
Expand All @@ -26,10 +30,76 @@ export async function loadConfig(): Promise<
: findUp.sync(CE_CTIX_DEFAULT_VALUE.TSCONFIG_FILENAME);

const configFilePath = getConfigFilePath(argv, tsconfigPath);
const parsed =
configFilePath != null ? parseConfig(await fs.promises.readFile(configFilePath)) : {};
const command = getCommand(argv._);
const config = castConfig(command, parsed, { config: configFilePath, tsconfig: tsconfigPath });

if (tsconfigPath == null) {
Spinner.it.fail('Cannot found tsconfig.json file!');
throw new Error('Cannot found tsconfig.json file!');
}

// case 1. using .ctirc
if (configFilePath != null) {
const parsed =
configFilePath != null ? parseConfig(await fs.promises.readFile(configFilePath)) : {};

const config = castConfig(command, parsed, {
config: configFilePath,
tsconfig: tsconfigPath,
});

Spinner.it.fail("load configuration from '.ctirc'");
return config;
}

// case 2. using tsconfig.json
const tsconfigParsed = await readJsonConfig<TsConfigJson>(tsconfigPath);
if (
configFilePath == null &&
tsconfigParsed != null &&
'ctix' in tsconfigParsed &&
tsconfigParsed.ctix != null &&
typeof tsconfigParsed.ctix === 'object' &&
Object.keys(tsconfigParsed.ctix).length > 0
) {
const config = castConfig(command, tsconfigParsed.ctix, {
config: configFilePath,
tsconfig: tsconfigPath,
});

Spinner.it.fail("load configuration from 'tsconfig.json'");
return config;
}

// case 3. using package.json
const packageJsonParsed = await readJsonConfig<PackageJson>(
posixJoin(process.cwd(), 'package.json'),
);
if (
configFilePath == null &&
packageJsonParsed != null &&
'ctix' in packageJsonParsed &&
packageJsonParsed.ctix != null &&
typeof packageJsonParsed.ctix === 'object' &&
Object.keys(packageJsonParsed.ctix).length > 0
) {
const config = castConfig(command, packageJsonParsed.ctix, {
config: configFilePath,
tsconfig: tsconfigPath,
});

Spinner.it.fail("load configuration from 'package.json'");
return config;
}

// case 4. in case of a read failure from .ctirc, tsconfig.json, or package.json
const config = castConfig(
command,
{},
{
config: configFilePath,
tsconfig: tsconfigPath,
},
);

return config;
} catch (catched) {
Expand Down
1 change: 1 addition & 0 deletions src/configs/modules/getDefaultInitAnswer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export async function getDefaultInitAnswer(): Promise<IInitQuestionAnswer> {
tsconfig: [tsconfigPath],
mode: CE_CTIX_BUILD_MODE.BUNDLE_MODE,
exportFilename: CE_CTIX_DEFAULT_VALUE.EXPORT_FILENAME,
configPosition: '.ctirc',
overwirte: true,
};

Expand Down
13 changes: 13 additions & 0 deletions src/configs/modules/json/readJsonConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { readJsonc } from '#/configs/modules/json/readJsonc';
import fs from 'node:fs';

export async function readJsonConfig<T = unknown>(jsonConfigFilePath: string) {
const buf = await fs.promises.readFile(jsonConfigFilePath);
const packageJsonParsed = readJsonc<T>(buf);

if (packageJsonParsed.type === 'fail') {
return undefined;
}

return packageJsonParsed.pass;
}
15 changes: 15 additions & 0 deletions src/configs/modules/json/safeJsonc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { readJsonc } from '#/configs/modules/json/readJsonc';

export function safeJsonc<T = unknown>(buf: Buffer | string): T | undefined {
try {
const json = readJsonc<T>(buf);

if (json.type === 'pass') {
return json.pass;
}

return undefined;
} catch {
return undefined;
}
}
63 changes: 60 additions & 3 deletions src/modules/commands/initializing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import { CE_CTIX_BUILD_MODE } from '#/configs/const-enum/CE_CTIX_BUILD_MODE';
import { CE_CTIX_DEFAULT_VALUE } from '#/configs/const-enum/CE_CTIX_DEFAULT_VALUE';
import type { TCommandInitOptions } from '#/configs/interfaces/TCommandInitOptions';
import { getDefaultInitAnswer } from '#/configs/modules/getDefaultInitAnswer';
import { safeJsonc } from '#/configs/modules/json/safeJsonc';
import { transformBundleMode } from '#/configs/transforms/transformBundleMode';
import { transformCreateMode } from '#/configs/transforms/transformCreateMode';
import { posixJoin } from '#/modules/path/modules/posixJoin';
import { prettifing } from '#/modules/writes/prettifing';
import { CE_TEMPLATE_NAME } from '#/templates/const-enum/CE_TEMPLATE_NAME';
import { TemplateContainer } from '#/templates/modules/TemplateContainer';
import chalk from 'chalk';
import { writeFile } from 'node:fs/promises';
import { exists } from 'my-node-fp';
import fs from 'node:fs';
import type { PackageJson, TsConfigJson } from 'type-fest';

export async function initializing(option: TCommandInitOptions) {
await TemplateContainer.bootstrap();
Expand Down Expand Up @@ -92,7 +95,61 @@ export async function initializing(option: TCommandInitOptions) {
endOfLine: 'lf',
});

await writeFile('.ctirc', prettified.contents);
if (answer.configPosition === 'tsconfig.json') {
// tsconfig.json 파일은 중요하니까, 백업 만들 생각이 있냐고 물어보자
await Promise.all(
answer.tsconfig.map(async (tsconfigFilePath) => {
const buf = await fs.promises.readFile(tsconfigFilePath);
const tsconfig = safeJsonc<TsConfigJson>(buf);

if (tsconfig != null) {
const newTsconfig = {
...tsconfig,
ctix: { ...(safeJsonc(Buffer.from(prettified.contents)) ?? {}) },
};

await fs.promises.writeFile(tsconfigFilePath, JSON.stringify(newTsconfig, null, 2));
Spinner.it.succeed(`${chalk.yellow(tsconfigFilePath)} file modifing completed!`);
}
}),
);
} else if (answer.configPosition === 'package.json') {
const packageJsonFilePath = (
await Promise.all(
[posixJoin(answer.cwd, 'package.json'), posixJoin(process.cwd(), 'package.json')].map(
async (filePath) => {
return {
exists: await exists(filePath),
filePath,
};
},
),
)
)
.filter((packageJson) => packageJson.exists)
.at(0);

if (packageJsonFilePath == null) {
throw new Error('Cannot found package.json file');
}

const buf = await fs.promises.readFile(packageJsonFilePath.filePath);
const packageJson = safeJsonc<PackageJson>(buf);

if (packageJson != null) {
const newPackageJson = {
...packageJson,
ctix: safeJsonc(Buffer.from(prettified.contents)) ?? {},
};

Spinner.it.succeed(`${chalk.yellow('.ctirc')} file writing completed!`);
await fs.promises.writeFile(
packageJsonFilePath.filePath,
JSON.stringify(newPackageJson, null, 2),
);
Spinner.it.succeed(`${chalk.yellow(packageJsonFilePath.filePath)} file modifing completed!`);
}
} else {
await fs.promises.writeFile('.ctirc', prettified.contents);
Spinner.it.succeed(`${chalk.yellow('.ctirc')} file writing completed!`);
}
}

0 comments on commit 5962b76

Please sign in to comment.