Skip to content

Commit

Permalink
feat: update dprint configuration and enhance prettier integration fo…
Browse files Browse the repository at this point in the history
…r improved code formatting
  • Loading branch information
Zoltan Erdos committed Feb 5, 2025
1 parent a96f8a0 commit 2b46133
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 99 deletions.
32 changes: 18 additions & 14 deletions dprint.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,9 @@
"indentWidth": 2,
"useTabs": false,
"newLineKind": "lf",
"typescript": {
"quoteStyle": "preferDouble",
"semiColons": "always",
"binaryExpression.operatorPosition": "sameLine",
"jsx.quoteStyle": "preferDouble",
"trailingCommas": "onlyMultiLine"
},
"json": {
"indentWidth": 2
},
"markdown": {},
"includes": ["**/*.{ts,tsx,js,jsx,cjs,mjs,json,md,json,html}"],
"includes": [
"**/*.{ts,tsx,js,jsx,cjs,mjs,json,md,html}"
],
"excludes": [
"**/tw.js",
"**/node_modules",
Expand All @@ -29,5 +20,18 @@
"https://plugins.dprint.dev/json-0.19.4.wasm",
"https://plugins.dprint.dev/dockerfile-0.3.2.wasm",
"https://plugins.dprint.dev/markdown-0.17.8.wasm"
]
}
],
"typescript": {
"quoteStyle": "preferDouble",
"semiColons": "always",
"binaryExpression.operatorPosition": "sameLine",
"jsx.quoteStyle": "preferDouble",
"trailingCommas": "onlyMultiLine"
},
"json": {
"indentWidth": 2
},
"markdown": {
"lineWidth": 100
}
}
198 changes: 114 additions & 84 deletions packages/code/src/@/lib/prettier.ts
Original file line number Diff line number Diff line change
@@ -1,123 +1,139 @@
import emotion from "@emotion/css-prettifier";
// import emotionPlugin from "@emotion/css-prettifier";
import type { Options } from "prettier";
import { format } from "prettier/standalone";
import pluginEstree from "prettier/plugins/estree";
import postCss from "prettier/plugins/postcss";
import pluginTypescript from "prettier/plugins/typescript";
import { format } from "prettier/standalone";
import postCss from "prettier/plugins/postcss";
import emotionPrettifier from "@emotion/css-prettifier";

/* ============================================================================
Utility Functions
========================================================================== */

/**
* Creates a string of spaces of the specified length.
* @param length - The number of spaces to create.
* Generates a string of spaces for a given length.
* @param length - The number of spaces.
* @returns A string of spaces.
*/
const createSpaceString = (length: number): string => " ".repeat(length);

const removeBlockComments = (code: string) => {
// Use a regular expression to match block comments
/**
* Removes block comments matching the pattern {/** ...
* @param code - The code to process.
* @returns The code without the block comments.
*/
const removeBlockComments = (code: string): string => {
const blockCommentRegex = /\{\/\*\*([\s\S]*?)\*\/\}/g;

// Replace all occurrences of block comments with an empty string
return code.replace(blockCommentRegex, "");
};

/**
* Processes and formats CSS-in-JS code, specifically dealing with the `css` template literal syntax.
* @param code - The input code string to process.
* @returns The processed and formated code string.
*/
export const addSomeFixesIfNeeded = (_code: string): string => {
const code = removeBlockComments(_code);
try {
const rest = code.split("css`");
let start = rest.shift() ?? "";

if (rest.length) {
if (!code.includes("@emotion/react") && !code.includes(" css ")) {
const [first, ...rest] = start.split("\n");
// insert the import to the 2nd line
if (first.startsWith("//")) {
start = [first, 'import { css } from "@emotion/react";', ...rest]
.join("\n");
} else {
start = ['import { css } from "@emotion/react";', first, ...rest]
.join("\n");
}
}
}

let prevIndent = (start.split("\n").pop()?.length ?? 0) + 2;

const processedParts = rest.map((part) => {
const [cssContent, afterCss, ...remainingParts] = part.split("`");
const indent = createSpaceString(prevIndent);
prevIndent = (afterCss.split("\n").pop()?.length ?? 0) + 2;

const formattedCssContent = formatCssContent(cssContent, indent);
return `${formattedCssContent}\n${indent}\`${[afterCss, ...remainingParts].join("`")}`;
});

let result = [start, ...processedParts].join("css`\n");
result = addDefaultExportIfNeeded(result);

return result;
} catch (error) {
console.error("addSomeFixesIfNeeded error", { error, code });
return code;
}
};
/* ============================================================================
CSS-in-JS Processing Functions
========================================================================== */

/**
* Formats the CSS content within the template literal, preserving interpolations.
* @param cssContent - The CSS content to format.
* @param indent - The indentation string to use.
* @returns The formatted CSS content with preserved interpolations.
* Formats CSS content within a template literal.
* It replaces interpolations with placeholders, formats the CSS,
* then restores the interpolations.
*
* @param cssContent - The raw CSS content.
* @param indent - The indentation string.
* @returns The formatted CSS content with interpolations restored.
*/
const formatCssContent = (cssContent: string, indent: string): string => {
// Replace interpolations with placeholders
const placeholderPrefix = "__INTERPOLATION_PLACEHOLDER_";
const interpolations: string[] = [];
let placeholderIndex = 0;

// Replace interpolations with unique placeholders.
const contentWithPlaceholders = cssContent.replace(/\${(.*?)}/g, (match) => {
const placeholder = `${placeholderPrefix}${placeholderIndex}`;
interpolations.push(match);
placeholderIndex++;
return placeholder;
});

// Format the CSS content
const formattedContent = emotion(contentWithPlaceholders)
// Format the CSS content using the emotion prettifier.
const formattedContent = emotionPrettifier(contentWithPlaceholders)
.split("\n")
.map((line: string) => line.trim() ? `${indent} ${line}` : line)
.map((line) => (line.trim() ? `${indent} ${line}` : line))
.join("\n");

// Restore interpolations
const finalContent = formattedContent.replace(
// Restore the interpolations.
return formattedContent.replace(
new RegExp(`${placeholderPrefix}\\d+`, "g"),
(match) => {
const index = parseInt(match.split("_").pop() || "0", 10);
const index = parseInt(match.replace(placeholderPrefix, ""), 10);
return interpolations[index];
},
}
);

return finalContent;
};

/**
* Adds a default export if it's missing and there's no named export.
* @param code - The code to check and potentially modify.
* @returns The code with a default export added if necessary.
* Adds a default export if none is found in the code.
*
* @param code - The code to check.
* @returns The code with a default export appended if necessary.
*/
const addDefaultExportIfNeeded = (code: string): string => {
if (
!code.includes("export default") && !code.includes("export const") &&
!code.includes("export default") &&
!code.includes("export const") &&
!code.includes("const App")
) {
return `${code}\n\nexport default () => <></>; // Empty default export`;
}
return code;
};

/**
* Processes and formats CSS-in-JS code that uses the css template literal.
* It also inserts an Emotion import if not present.
*
* @param rawCode - The original code string.
* @returns The processed and formatted code.
*/
export const addSomeFixesIfNeeded = (rawCode: string): string => {
const code = removeBlockComments(rawCode);
try {
const parts = code.split("css`");
let header = parts.shift() || "";

// Insert Emotion's css import if it's missing.
if (parts.length && !code.includes("@emotion/react") && !code.includes(" css ")) {
const [firstLine, ...restLines] = header.split("\n");
header = firstLine.startsWith("//")
? [firstLine, 'import { css } from "@emotion/react";', ...restLines].join("\n")
: ['import { css } from "@emotion/react";', firstLine, ...restLines].join("\n");
}

let currentIndent = (header.split("\n").pop()?.length || 0) + 2;

// Process each css template literal part.
const processedSections = parts.map((section) => {
const [cssContent, afterCss, ...remainingParts] = section.split("`");
const indent = createSpaceString(currentIndent);
currentIndent = (afterCss.split("\n").pop()?.length || 0) + 2;

const formattedCss = formatCssContent(cssContent, indent);
return `${formattedCss}\n${indent}\`${[afterCss, ...remainingParts].join("`")}`;
});

let result = [header, ...processedSections].join("css`\n");
result = addDefaultExportIfNeeded(result);
return result;
} catch (error) {
console.error("addSomeFixesIfNeeded error", { error, code });
return code;
}
};

/* ============================================================================
Prettier Formatting Functions & Configuration
========================================================================== */

/**
* Prettier configuration for JavaScript/TypeScript.
*/
const prettierConfig: Options = {
arrowParens: "always",
bracketSpacing: true,
Expand All @@ -135,26 +151,40 @@ const prettierConfig: Options = {
tabWidth: 2,
trailingComma: "es5",
useTabs: false,
parser: "typescript", // Prettier will infer the parser based on file extension
parser: "typescript",
singleAttributePerLine: false,
plugins: [pluginEstree, pluginTypescript // emotionPlugin
],
plugins: [pluginEstree, pluginTypescript]
};

export const prettierJs = async (
{ code, toThrow }: { code: string; toThrow: boolean; },
) => {
/**
* Formats JavaScript/TypeScript code using Prettier.
*
* @param params.code - The code to format.
* @param params.toThrow - If true, errors are thrown.
* @returns The formatted code.
*/
export const prettierJs = async ({
code,
toThrow,
}: {
code: string;
toThrow: boolean;
}): Promise<string> => {
try {
return await format(code, prettierConfig);
} catch (error) {
if (toThrow) {
throw error;
}
if (toThrow) throw error;
return code;
}
};

export const prettierCss = async (inputCSS: string) =>
/**
* Formats CSS code using Prettier with a CSS parser.
*
* @param inputCSS - The CSS code to format.
* @returns The formatted CSS.
*/
export const prettierCss = async (inputCSS: string): Promise<string> =>
format(inputCSS, {
parser: "css",
printWidth: 80,
Expand All @@ -163,4 +193,4 @@ export const prettierCss = async (inputCSS: string) =>
singleQuote: false,
trailingComma: "none",
plugins: [postCss],
});
});
4 changes: 3 additions & 1 deletion packages/code/src/reactMod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import React from "react";
import { useSyncExternalStore } from 'react';

export { useSyncExternalStore };

export const {
Children,
Expand Down Expand Up @@ -31,7 +34,6 @@ export const {
useReducer,
useRef,
useState,
useSyncExternalStore,
useTransition,
cache,
use,
Expand Down

0 comments on commit 2b46133

Please sign in to comment.