Skip to content
4 changes: 2 additions & 2 deletions packages/adders/eslint/config/adder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const adder = defineAdderConfig({
name: 'eslint-config-prettier',
version: '^9.1.0',
dev: true,
condition: ({ prettier }) => prettier
condition: ({ dependencyVersion }) => Boolean(dependencyVersion('prettier'))
}
],
files: [
Expand Down Expand Up @@ -141,7 +141,7 @@ export const adder = defineAdderConfig({
{
name: () => 'eslint.config.js',
contentType: 'script',
condition: ({ prettier }) => prettier,
condition: ({ dependencyVersion }) => Boolean(dependencyVersion('prettier')),
content: addEslintConfigPrettier
}
]
Expand Down
17 changes: 9 additions & 8 deletions packages/adders/prettier/config/adder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const adder = defineAdderConfig({
name: 'eslint-config-prettier',
version: '^9.1.0',
dev: true,
condition: ({ dependencies }) => hasEslint(dependencies)
condition: ({ dependencyVersion }) => hasEslint(dependencyVersion)
}
],
files: [
Expand Down Expand Up @@ -69,13 +69,13 @@ export const adder = defineAdderConfig({
{
name: () => 'package.json',
contentType: 'json',
content: ({ data, dependencies }) => {
content: ({ data, dependencyVersion }) => {
data.scripts ??= {};
const scripts: Record<string, string> = data.scripts;
const CHECK_CMD = 'prettier --check .';
scripts['format'] ??= 'prettier --write .';

if (hasEslint(dependencies)) {
if (hasEslint(dependencyVersion)) {
scripts['lint'] ??= `${CHECK_CMD} && eslint .`;
if (!scripts['lint'].includes(CHECK_CMD)) scripts['lint'] += ` && ${CHECK_CMD}`;
} else {
Expand All @@ -86,17 +86,17 @@ export const adder = defineAdderConfig({
{
name: () => 'eslint.config.js',
contentType: 'script',
condition: ({ dependencies: deps }) => {
condition: ({ dependencyVersion }) => {
// We only want this to execute when it's `false`, not falsy

if (deps['eslint']?.startsWith(SUPPORTED_ESLINT_VERSION) === false) {
if (dependencyVersion('eslint')?.startsWith(SUPPORTED_ESLINT_VERSION) === false) {
log.warn(
`An older major version of ${colors.yellow(
'eslint'
)} was detected. Skipping ${colors.yellow('eslint-config-prettier')} installation.`
);
}
return hasEslint(deps);
return hasEslint(dependencyVersion);
},
content: addEslintConfigPrettier
}
Expand All @@ -105,6 +105,7 @@ export const adder = defineAdderConfig({

const SUPPORTED_ESLINT_VERSION = '9';

function hasEslint(deps: Record<string, string>): boolean {
return !!deps['eslint'] && deps['eslint'].startsWith(SUPPORTED_ESLINT_VERSION);
function hasEslint(dependencyVersion: (pkg: string) => string | undefined): boolean {
const version = dependencyVersion('eslint');
return !!version && version.startsWith(SUPPORTED_ESLINT_VERSION);
}
4 changes: 2 additions & 2 deletions packages/adders/tailwindcss/config/adder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const adder = defineAdderConfig({
name: 'prettier-plugin-tailwindcss',
version: '^0.6.5',
dev: true,
condition: ({ prettier }) => prettier
condition: ({ dependencyVersion }) => Boolean(dependencyVersion('prettier'))
}
],
files: [
Expand Down Expand Up @@ -134,7 +134,7 @@ export const adder = defineAdderConfig({

if (!plugins.includes(PLUGIN_NAME)) plugins.push(PLUGIN_NAME);
},
condition: ({ prettier }) => prettier
condition: ({ dependencyVersion }) => Boolean(dependencyVersion('prettier'))
}
]
});
8 changes: 6 additions & 2 deletions packages/cli/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ export async function runAddCommand(options: Options, adders: string[]): Promise
let installed = false;
installed = dependent.config.packages.every(
// we'll skip the conditions since we don't have any options to supply it
(p) => p.condition !== undefined || !!workspace.dependencies[p.name]
(p) => p.condition !== undefined || !!workspace.dependencyVersion(p.name)
);

if (installed) continue;
Expand Down Expand Up @@ -463,7 +463,11 @@ export async function runAddCommand(options: Options, adders: string[]): Promise

// format modified/created files with prettier (if available)
const workspace = createWorkspace(options.cwd);
if (filesToFormat.length > 0 && depsStatus === 'installed' && workspace.prettier) {
if (
filesToFormat.length > 0 &&
depsStatus === 'installed' &&
!!workspace.dependencyVersion('prettier')
) {
const { start, stop } = p.spinner();
start('Formatting modified files');
try {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/files/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
type CssAst,
type HtmlDocument
} from '@svelte-cli/ast-tooling';
import { fileExistsWorkspace, readFile, writeFile } from './utils.ts';
import { fileExists, readFile, writeFile } from './utils.ts';
import type { ConditionDefinition } from '../adder/config.ts';
import type { OptionDefinition } from '../adder/options.ts';
import type { Workspace } from './workspace.ts';
Expand Down Expand Up @@ -89,8 +89,8 @@ export function createOrUpdateFiles<Args extends OptionDefinition>(
continue;
}

const exists = fileExistsWorkspace(workspace, fileDetails.name(workspace));
let content = exists ? readFile(workspace, fileDetails.name(workspace)) : '';
const exists = fileExists(workspace.cwd, fileDetails.name(workspace));
let content = exists ? readFile(workspace.cwd, fileDetails.name(workspace)) : '';

if (fileDetails.contentType === 'css') {
content = handleCssFile(content, fileDetails, workspace);
Expand Down
17 changes: 9 additions & 8 deletions packages/core/files/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ export type Package = {
bugs?: string;
repository?: { type: string; url: string };
keywords?: string[];
workspaces?: string[];
};

export function getPackageJson(workspace: Workspace<any>): {
export function getPackageJson(cwd: string): {
text: string;
data: Package;
} {
const packageText = readFile(workspace, commonFilePaths.packageJson);
const packageText = readFile(cwd, commonFilePaths.packageJson);
if (!packageText) {
return {
text: '',
Expand All @@ -39,10 +40,10 @@ export function getPackageJson(workspace: Workspace<any>): {
};
}

export function readFile(workspace: Workspace<any>, filePath: string): string {
const fullFilePath = getFilePath(workspace.cwd, filePath);
export function readFile(cwd: string, filePath: string): string {
const fullFilePath = getFilePath(cwd, filePath);

if (!fileExistsWorkspace(workspace, filePath)) {
if (!fileExists(cwd, filePath)) {
return '';
}

Expand All @@ -52,7 +53,7 @@ export function readFile(workspace: Workspace<any>, filePath: string): string {
}

export function installPackages(config: AdderConfig<any>, workspace: Workspace<any>): string {
const { text: originalText, data } = getPackageJson(workspace);
const { text: originalText, data } = getPackageJson(workspace.cwd);

for (const dependency of config.packages) {
if (dependency.condition && !dependency.condition(workspace)) {
Expand Down Expand Up @@ -103,8 +104,8 @@ export function writeFile(workspace: Workspace<any>, filePath: string, content:
fs.writeFileSync(fullFilePath, content, 'utf8');
}

export function fileExistsWorkspace(workspace: Workspace<any>, filePath: string): boolean {
const fullFilePath = getFilePath(workspace.cwd, filePath);
export function fileExists(cwd: string, filePath: string): boolean {
const fullFilePath = getFilePath(cwd, filePath);
return fs.existsSync(fullFilePath);
}

Expand Down
55 changes: 40 additions & 15 deletions packages/core/files/workspace.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import * as find from 'empathic/find';
import * as resolve from 'empathic/resolve';
import { AGENTS, detectSync, type AgentName } from 'package-manager-detector';
import { type AstTypes, parseScript } from '@svelte-cli/ast-tooling';
import { TESTING } from '../env.ts';
Expand All @@ -13,8 +12,13 @@ import process from 'node:process';
export type Workspace<Args extends OptionDefinition> = {
options: OptionValues<Args>;
cwd: string;
dependencies: Record<string, string>;
prettier: boolean;
/**
* Returns the dependency version declared in the package.json.
* This may differ from the installed version.
* @param pkg the package to check for
* @returns the dependency version with any leading characters such as ^ or ~ removed
*/
dependencyVersion: (pkg: string) => string | undefined;
typescript: boolean;
kit: { libDirectory: string; routesDirectory: string } | undefined;
packageManager: AgentName;
Expand All @@ -24,7 +28,7 @@ export function createEmptyWorkspace<Args extends OptionDefinition>() {
return {
options: {},
cwd: '',
prettier: false,
dependencyVersion: (_pkg) => undefined,
typescript: false,
kit: undefined
} as Workspace<Args>;
Expand All @@ -44,23 +48,44 @@ export function createWorkspace<Args extends OptionDefinition>(cwd: string): Wor
usesTypescript ||= find.up(commonFilePaths.tsconfig, { cwd }) !== undefined;
}

const { data: packageJson } = getPackageJson(workspace);

workspace.dependencies = { ...packageJson.devDependencies, ...packageJson.dependencies };
workspace.dependencyVersion = (pkg) => {
const root = findRoot(workspace.cwd);
let directory = cwd;
while (directory && directory !== root) {
const { data: packageJson } = getPackageJson(workspace.cwd);
const dependencies = { ...packageJson.devDependencies, ...packageJson.dependencies };
const found = dependencies[pkg];
if (found) {
// removes the version ranges (e.g. `^` is removed from: `^9.0.0`)
return found.replaceAll(/[^\d|.]/g, '');
}
directory = path.dirname(directory);
}
};
workspace.typescript = usesTypescript;
workspace.prettier = Boolean(resolve.from(cwd, 'prettier', true));
workspace.packageManager = detectPackageManager(cwd);
if ('@sveltejs/kit' in workspace.dependencies) workspace.kit = parseKitOptions(workspace);
for (const [key, value] of Object.entries(workspace.dependencies)) {
// removes the version ranges (e.g. `^` is removed from: `^9.0.0`)
workspace.dependencies[key] = value.replaceAll(/[^\d|.]/g, '');
}

if (workspace.dependencyVersion('@sveltejs/kit')) workspace.kit = parseKitOptions(workspace);
return workspace;
}

function findRoot(cwd: string): string {
const { root } = path.parse(cwd);
let directory = cwd;
while (directory && directory !== root) {
if (fs.existsSync(path.join(directory, 'pnpm-workspace.yaml'))) {
return directory;
}
const { data } = getPackageJson(directory);
if (data.workspaces) {
return directory;
}
directory = path.dirname(directory);
}
return root;
}

function parseKitOptions(workspace: Workspace<any>) {
const configSource = readFile(workspace, commonFilePaths.svelteConfig);
const configSource = readFile(workspace.cwd, commonFilePaths.svelteConfig);
const ast = parseScript(configSource);

const defaultExport = ast.body.find((s) => s.type === 'ExportDefaultDeclaration');
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.