Skip to content

Commit

Permalink
feat(biome): parse biome config
Browse files Browse the repository at this point in the history
Biome config is now available

Closes: #2
  • Loading branch information
Sukaato committed Aug 11, 2024
1 parent c71dba8 commit e0b4167
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 41 deletions.
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"arrowParens": "avoid",
"bracketSameLine": false,
"bracketSpacing": true,
Expand Down
32 changes: 18 additions & 14 deletions src/component.target.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { parseFlags } from '@stencil/core/cli';
import { loadConfig } from '@stencil/core/compiler';
import { BuildCtx, Config, OutputTargetCustom } from '@stencil/core/internal';
import { writeFileSync } from 'fs';
import { join } from 'path';
import { isSingleQuoteUsed } from './prettier.config';
import { writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { parseConfig } from './config-parser';
import { hasConfigProp, typeImportData } from './util';

interface ComponentConfigOptions {
Expand All @@ -29,9 +29,13 @@ export function componentConfigTarget(options?: ComponentConfigOptions): OutputT
}

async function generateDts(options: ComponentConfigOptions, config: Config, buildCtx: BuildCtx) {
const useSingleQuote = await isSingleQuoteUsed();
const QUOTE = useSingleQuote ? `'` : `"`;
const styleConfig = await parseConfig();
const QUOTE = styleConfig.isSingleQuote ? `'` : `"`;
const quote = (text: string) => `${QUOTE}${text}${QUOTE}`;
const semi = styleConfig.useSemi ? ';' : '';
const indentStyle = styleConfig.tabIndent ? '\t' : Array(styleConfig.tabSize).fill(' ').join('');
const indent = (size: number) => Array(size).fill(indentStyle).join('');

const content: string[] = [
'/* eslint-disable */',
'/* tslint:disable */',
Expand All @@ -50,30 +54,30 @@ async function generateDts(options: ComponentConfigOptions, config: Config, buil
sys: config.sys,
});
const types = typeImportData(validated.config, buildCtx);
types.forEach(type => content.push(`import ${type}`));
types.forEach(type => content.push(`import type ${type}`));
content.push('');

content.push('export namespace Configuration {');
content.push(' interface ComponentsConfig {');
content.push(`${indent(1)}interface ComponentsConfig {`);
buildCtx.components.forEach(component => {
const props = component.properties.filter(hasConfigProp);
if (props.length === 0) {
return;
}
const tagName = options.prefix === false ? component.tagName.split('-').splice(1).join('-') : component.tagName;
content.push(` ${quote(tagName)}?: {`);
content.push(`${indent(2)}${quote(tagName)}?: {`);

props.forEach(prop => {
if (prop.docs.text) {
content.push(' /**');
prop.docs.text.split(/[\r\n]+/).forEach(line => content.push(` * ${line}`));
content.push(' */');
content.push(`${indent(3)}/**`);
prop.docs.text.split(/[\r\n]+/).forEach(line => content.push(`${indent(3)} * ${line}`));
content.push(`${indent(3)} */`);
}
content.push(` ${prop.name}?: ${prop.complexType.original};`);
content.push(`${indent(3)}${prop.name}?: ${prop.complexType.original}${semi}`);
});
content.push(' }');
content.push(`${indent(2)}}${semi}`);
});
content.push(' }');
content.push(`${indent(1)}}`);
content.push('}');

return content.join('\n');
Expand Down
54 changes: 54 additions & 0 deletions src/config-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { existsSync } from "node:fs";
import { join } from "node:path";
import { parseBiomeConfig } from "./parser/biome";
import { parsePrettierConfig } from "./parser/prettier";
import { ConfigType, type StyleConfig } from "./types/config";


export async function parseConfig(): Promise<StyleConfig> {
const configType = getConfigType();
return getConfig(configType)
}

function getConfigType(): ConfigType {
const prettierrc = existsSync(join(process.cwd(), ConfigType.PRETTIER));
if (prettierrc) {
return ConfigType.PRETTIER;
}

const prettierrcjson = existsSync(join(process.cwd(), ConfigType.PRETTIER_JSON));
if (prettierrcjson) {
return ConfigType.PRETTIER_JSON;
}

const biomeJson = existsSync(join(process.cwd(), ConfigType.BIOME_JSON));
if (biomeJson) {
return ConfigType.BIOME_JSON;
}

const biomeJsonc = existsSync(join(process.cwd(), ConfigType.BIOME_JSONC));
if (biomeJsonc) {
return ConfigType.BIOME_JSONC;
}

return ConfigType.UNKNOWN;
}

async function getConfig(type: ConfigType): Promise<StyleConfig> {
switch (type) {
case ConfigType.PRETTIER:
case ConfigType.PRETTIER_JSON:
return parsePrettierConfig(type);
case ConfigType.BIOME_JSON:
case ConfigType.BIOME_JSONC:
return parseBiomeConfig(type);
default:
return {
isSingleQuote: true,
tabIndent: false,
tabSize: 2,
useSemi: true,
};
}
}

84 changes: 84 additions & 0 deletions src/parser/biome.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import type { Biome } from "../types/biome";
import { ConfigType, type StyleConfig } from "../types/config";

const FILE_PATTERN = /\*(?:\.d)?\.ts$/;

export async function parseBiomeConfig(type: ConfigType): Promise<StyleConfig> {
const configs = await retriveExtendsConfig(type);

return {
isSingleQuote: isSingleQuote(configs),
useSemi: useSemi(configs),
tabIndent: useTabs(configs),
tabSize: indentSize(configs),
};
}

async function retriveExtendsConfig(...paths: string[]): Promise<Biome[]> {
const files = await Promise.all(
paths.map(path => (
readFile(join(process.cwd(), path), { encoding: 'utf-8' })
))
);
const configs: Biome[] = files.map(file => JSON.parse(file));
for (const config of configs) {
if ('extends' in config && config.extends.length > 0) {
config.extends = await retriveExtendsConfig(...config.extends as string[])
}
}

return configs;
}

function isSingleQuote(configs: Biome[]): boolean {
return configs.some(config => (
config.override
.filter(override => override.include.some(file => FILE_PATTERN.test(file)))
.some(override => override.javascript?.formatter.quoteStyle === 'single')

|| config.javascript?.formatter.quoteStyle === 'single'
|| isSingleQuote(config.extends as Biome[])
));
}

function useSemi(configs: Biome[]): boolean {
return configs.some(config => (
config.override
.filter(override => override.include.some(file => FILE_PATTERN.test(file)))
.some(override => override.javascript?.formatter.semicolons === 'always')

|| config.javascript?.formatter.semicolons === 'always'
|| useSemi(config.extends as Biome[])
));
}

function useTabs(configs: Biome[]): boolean {
return configs.some(config => (
config.override
.filter(override => override.include.some(file => FILE_PATTERN.test(file)))
.some(override => (
override.javascript?.formatter.indentStyle === 'tab'
|| override.formatter?.indentStyle === 'tab'
))

|| config.javascript?.formatter.indentStyle === 'tab'
|| config.formatter?.indentStyle === 'tab'
|| useTabs(config.extends as Biome[])
));
}

function indentSize(configs: Biome[]): number {
return configs.map(config => {
const overrideSize = config.override
.filter(override => override.include.some(file => FILE_PATTERN.test(file)))
.filter(override => ('javascript' in override && 'formatter' in override.javascript!) || 'formatter' in override)
.map(override => override.javascript?.formatter.indentWidth || override.formatter?.indentWidth).at(-1); // We assum the last one is the right to use

const jsFormatterSize = config.javascript?.formatter.indentWidth;
const formatterSize = config.formatter?.indentWidth;

return overrideSize || jsFormatterSize || formatterSize || indentSize(config.extends as Biome[])
}).at(-1) || 2 // Same we assum the last found is the one to use
}
36 changes: 36 additions & 0 deletions src/parser/prettier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import { ConfigType, type StyleConfig } from "../types/config";
import type { Prettier } from "../types/prettier";

export async function parsePrettierConfig(type: ConfigType): Promise<StyleConfig> {
const file = await readFile(join(process.cwd(), type), { encoding: 'utf-8' })
const config: Prettier = JSON.parse(file);

const isSingleQuote: boolean =
config.overrides?.find(override => override.files === '*.d.ts' || override.files === '*.ts')?.options.singleQuote ??
config.singleQuote ??
false;

const useSemi: boolean =
config.overrides?.find(override => override.files === '*.d.ts' || override.files === '*.ts')?.options.semi ??
config.semi ??
false;

const useTabs: boolean =
config.overrides?.find(override => override.files === '*.d.ts' || override.files === '*.ts')?.options.useTabs ??
config.useTabs ??
false;

const indentSize: number =
config.overrides?.find(override => override.files === '*.d.ts' || override.files === '*.ts')?.options.tabWidth ??
config.tabWidth ??
2;

return {
isSingleQuote,
useSemi,
tabIndent: useTabs,
tabSize: indentSize,
};
}
27 changes: 0 additions & 27 deletions src/prettier.config.ts

This file was deleted.

25 changes: 25 additions & 0 deletions src/types/biome.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export interface Biome {
extends: string[] | Biome[];
formatter?: BiomeFormatter;
javascript?: BiomeJavascript;
override: BiomeOverride[];
}


interface BiomeFormatter {
indentWidth?: number;
indentStyle?: 'tab' | 'space';
}


interface BiomeJavascript {
formatter: BiomeJsFormatter;
}
type BiomeJsFormatter = BiomeFormatter & {
semicolons?: 'always' | 'asNeeded';
quoteStyle: 'single' | 'double'
}

type BiomeOverride = Omit<Biome, 'override' | 'extends'> & {
include: string[];
}
18 changes: 18 additions & 0 deletions src/types/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export enum ConfigType {
PRETTIER = '.prettierrc',
PRETTIER_JSON = '.prettierrc.json',
BIOME_JSON = 'biome.json',
BIOME_JSONC = 'biome.jsonc',
UNKNOWN = 'unknown',
}

export interface StyleConfig {
isSingleQuote: boolean;
useSemi: boolean;
tabIndent: boolean;

/**
* used when `tabIndent` is `false`
*/
tabSize: number;
}
13 changes: 13 additions & 0 deletions src/types/prettier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface Prettier {
singleQuote?: boolean;
semi?: boolean;
tabWidth?: number;
useTabs?: boolean;

overrides?: PrettierOverride[];
}

interface PrettierOverride {
files: string;
options: Omit<Prettier, 'overrides'>;
}

0 comments on commit e0b4167

Please sign in to comment.