diff --git a/configuration/biome.ts b/configuration/biome.ts index 869ef86..b514007 100644 --- a/configuration/biome.ts +++ b/configuration/biome.ts @@ -1,6 +1,6 @@ export const templates = { recommended: { - $schema: 'https://biomejs.dev/schemas/1.6.4/schema.json', + $schema: 'node_modules/@biomejs/biome/configuration_schema.json', organizeImports: { enabled: true, }, @@ -10,6 +10,7 @@ export const templates = { all: true, style: { useBlockStatements: 'off', + noNamespaceImport: 'off', }, }, }, diff --git a/configuration/eslint.ts b/configuration/eslint.ts index 5dbdd12..23aa12e 100644 --- a/configuration/eslint.ts +++ b/configuration/eslint.ts @@ -4,6 +4,8 @@ export const templates = { }, } +export const extension = (path: string) => ({ extends: path }) + export function createFile(configuration: object) { - return { name: 'eslint.config.js', contents: `export default [${configuration}]` } + return { name: 'eslint.config.js', contents: `export default [${JSON.stringify(configuration, null, 2)}]` } } diff --git a/configuration/typescript.ts b/configuration/typescript.ts index 75529d8..921942c 100644 --- a/configuration/typescript.ts +++ b/configuration/typescript.ts @@ -6,6 +6,8 @@ export const templates = { }, } +export const extension = (path: string) => ({ extends: path }) + export function createFile(configuration: object) { return { name: 'tsconfig.json', contents: JSON.stringify(configuration, null, 2) } } diff --git a/index.ts b/index.ts index 154bbff..9438210 100644 --- a/index.ts +++ b/index.ts @@ -11,6 +11,7 @@ import * as playwright from './configuration/playwright' import * as prettier from './configuration/prettier' import * as typescript from './configuration/typescript' import * as vscode from './configuration/vscode' +import { parse } from './parse' import type { Configuration } from './types' const log = create('zero-configuration', 'blue') @@ -60,25 +61,10 @@ const userConfiguration = packageJson.configuration ?? moduleContents for (const { name, alias, configuration } of configurations) { const value = userConfiguration[name] ?? (alias && userConfiguration[alias]) if (!value) continue - - if (typeof value === 'string' && Object.hasOwn(configuration.templates, value)) { - const configurationTemplate = configuration.templates[value as keyof typeof configuration.templates] - const file = configuration.createFile(configurationTemplate) - await Bun.write(file.name, file.contents) - ignores.push(file.name) - } - - if (typeof value === 'object') { - const file = configuration.createFile(value) - await Bun.write(file.name, file.contents) - ignores.push(file.name) - } - - if (value === true) { - const file = configuration.createFile() - await Bun.write(file.name, file.contents) - ignores.push(file.name) - } + const file = await parse(value, configuration) + if (!file) continue + await Bun.write(file.name, file.contents) + ignores.push(file.name) } if (ignores.length === 0) { diff --git a/package.json b/package.json index 43b88d3..cc8fcf8 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,9 @@ "logua": "^3.0.3" }, "devDependencies": { - "@biomejs/biome": "^1.6.4", - "@types/bun": "^1.0.12", + "@biomejs/biome": "^1.7.0", + "@types/bun": "^1.1.0", + "eslint-config-airbnb": "^19.0.4", "typescript": "^5.4.5" }, "peerDependencies": { diff --git a/parse.ts b/parse.ts new file mode 100644 index 0000000..aa0ae20 --- /dev/null +++ b/parse.ts @@ -0,0 +1,35 @@ +import { existsSync } from 'node:fs' +import { join } from 'node:path' +import { it } from 'avait' +import type { Configuration } from './types' + +const isExtension = async (value: string) => { + // NOTE dynamic import in tests will resolve relative to project node_modules and not fixture. + const { error } = await it(import(value)) + if (!error) return true + const paths = [value, join(process.cwd(), value), join(process.cwd(), 'node_modules', value)] + const fileExists = paths.some(existsSync) + if (fileExists) return true + return false +} + +export async function parse(value: string | object | boolean, configuration: Configuration['configuration']) { + // Template. + if (typeof value === 'string' && Object.hasOwn(configuration.templates, value)) { + const configurationTemplate = configuration.templates[value as keyof typeof configuration.templates] + return configuration.createFile(configurationTemplate) + } + + // File extension. + if (typeof value === 'string' && (await isExtension(value)) && typeof configuration.extension === 'function') { + return configuration.createFile(configuration.extension(value)) + } + + if (typeof value === 'object') { + return configuration.createFile(value) + } + + if (value === true) { + return configuration.createFile() + } +} diff --git a/test/basic.test.ts b/test/basic.test.ts index a5d0e64..ca2f8fa 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -25,3 +25,15 @@ test('Adds configuration files for basic file setup.', () => { expect(existsSync(join(fixturePath, 'prettier.config.js'))).toBe(true) }) + +test('Extends existing configurations.', () => { + const fixturePath = './test/fixture/extends' + + execSync('bun ./../../../index.ts', { + cwd: fixturePath, + stdio: 'inherit', + }) + + expect(existsSync(join(fixturePath, 'eslint.config.js'))).toBe(true) + expect(existsSync(join(fixturePath, 'tsconfig.json'))).toBe(true) +}) diff --git a/test/fixture/extends/package.json b/test/fixture/extends/package.json new file mode 100644 index 0000000..4f8cfa1 --- /dev/null +++ b/test/fixture/extends/package.json @@ -0,0 +1,7 @@ +{ + "name": "extends", + "configuration": { + "eslint": "eslint-config-airbnb", + "typescript": "my-shared-tsconfig/tsconfig.json" + } +} diff --git a/test/fixture/file/configuration.ts b/test/fixture/file/configuration.ts index feb3698..c0d347a 100644 --- a/test/fixture/file/configuration.ts +++ b/test/fixture/file/configuration.ts @@ -1,4 +1,4 @@ -import { devices } from '@playwright/test' +// NOTE imports will fail in fixture. export const prettier = 'recommended' @@ -8,7 +8,6 @@ export const playwright = { projects: [ { name: 'chromium', - use: { ...devices['Desktop Chrome'] }, }, ], webServer: { diff --git a/types.ts b/types.ts index 506beda..72517ec 100644 --- a/types.ts +++ b/types.ts @@ -7,5 +7,6 @@ export type Configuration = { templates: Template // biome-ignore lint/suspicious/noExplicitAny: Will be specified in file explicitly. createFile: (value?: any) => { name: string; contents: string } + extension?: (path: string) => object } }