Skip to content

Commit

Permalink
Merge pull request #23 from ony3000/semi-rework-again
Browse files Browse the repository at this point in the history
Partially reworked the plugin
  • Loading branch information
ony3000 authored Jan 18, 2024
2 parents ec98a01 + 9b53190 commit 409b956
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 198 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ For Prettier v2:
npm install -D prettier@^2 prettier-plugin-classnames
```

For Prettier v3:
For Prettier v3:[^1]

```sh
npm install -D prettier prettier-plugin-classnames @prettier/sync
npm install -D prettier prettier-plugin-classnames
```

[^1]: If your version of `prettier-plugin-classnames` is less than `0.4.0`, you will also need to install `@prettier/sync`.

## Configuration

JSON:
Expand Down
7 changes: 0 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"prepare-release": "pnpm run build:plain && npm version prerelease --preid=alpha --git-tag-version=false && pnpm run merge-deps && npm pack"
},
"devDependencies": {
"@prettier/sync": "0.3.0",
"@trivago/prettier-plugin-sort-imports": "4.2.1",
"@types/node": "20.2.5",
"@types/prettier": "2.7.3",
Expand All @@ -50,14 +49,8 @@
"zod": "3.22.4"
},
"peerDependencies": {
"@prettier/sync": "^0.3.0",
"prettier": "^2 || ^3"
},
"peerDependenciesMeta": {
"@prettier/sync": {
"optional": true
}
},
"packageManager": "[email protected]",
"engines": {
"node": ">=14"
Expand Down
22 changes: 0 additions & 22 deletions pnpm-lock.yaml

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

97 changes: 97 additions & 0 deletions src/packages/core-parts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -762,3 +762,100 @@ export function parseLineByLineAndReplace(
format,
);
}

async function replaceClassNameAsync(
formattedText: string,
indentUnit: string,
targetClassNameNodes: ClassNameNode[],
lineNodes: LineNode[],
options: NarrowedParserOptions,
format: (source: string, options?: any) => Promise<string>,
): Promise<string> {
const mutableFormattedText = await targetClassNameNodes.reduce(
async (formattedPrevTextPromise, { type, range: [rangeStart, rangeEnd], startLineIndex }) => {
const formattedPrevText = await formattedPrevTextPromise;

const { indentLevel } = lineNodes[startLineIndex];
const enclosedClassName = formattedPrevText.slice(rangeStart + 1, rangeEnd - 1);
const formattedClassName = (
await format(enclosedClassName, {
...options,
parser: 'html',
plugins: [],
rangeStart: 0,
rangeEnd: Infinity,
endOfLine: 'lf',
})
).trimEnd();

if (formattedClassName === enclosedClassName) {
return formattedPrevTextPromise;
}

let extraIndentLevel = 0;

if (type === ClassNameType.ASL) {
extraIndentLevel = 2;
} else if (
[
ClassNameType.AOL,
ClassNameType.SLSL,
ClassNameType.SLTO,
ClassNameType.CTL,
ClassNameType.TLTO,
].includes(type)
) {
extraIndentLevel = 1;
}

const quoteStart = `${type === ClassNameType.SLOP ? '[' : ''}${
type === ClassNameType.ASL || type === ClassNameType.AOL ? '"' : '`'
}`;
const quoteEnd = `${type === ClassNameType.ASL || type === ClassNameType.AOL ? '"' : '`'}${
type === ClassNameType.SLOP ? ']' : ''
}`;
const substitute = `${quoteStart}${formattedClassName}${quoteEnd}`
.split(EOL)
.join(`${EOL}${indentUnit.repeat(indentLevel + extraIndentLevel)}`);

return `${formattedPrevText.slice(0, rangeStart)}${substitute}${formattedPrevText.slice(
rangeEnd,
)}`;
},
Promise.resolve(formattedText),
);

return mutableFormattedText;
}

export async function parseLineByLineAndReplaceAsync(
formattedText: string,
ast: any,
options: NarrowedParserOptions,
format: (source: string, options?: any) => Promise<string>,
addon: Dict<(text: string, options: any) => any>,
): Promise<string> {
if (formattedText === '') {
return formattedText;
}

const indentUnit = options.useTabs ? '\t' : ' '.repeat(options.tabWidth);

let targetClassNameNodes: ClassNameNode[] = [];
if (options.parser === 'vue') {
targetClassNameNodes = findTargetClassNameNodesForVue(ast, options, addon);
} else {
targetClassNameNodes = findTargetClassNameNodes(ast, options);
}

const lineNodes = parseLineByLine(formattedText, indentUnit);

return replaceClassNameAsync(
formattedText,
indentUnit,
targetClassNameNodes,
lineNodes,
options,
format,
);
}
72 changes: 59 additions & 13 deletions src/packages/v2-plugin/parsers.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,65 @@
import type { Parser } from 'prettier';
import { parseLineByLineAndReplace } from 'core-parts';
import type { Parser, ParserOptions } from 'prettier';
import { format } from 'prettier';
import { parsers as babelParsers } from 'prettier/parser-babel';
import { parsers as htmlParsers } from 'prettier/parser-html';
import { parsers as typescriptParsers } from 'prettier/parser-typescript';

const addon = {
parseBabel: (text: string, options: ParserOptions) =>
babelParsers.babel.parse(text, { babel: babelParsers.babel }, options),
parseTypescript: (text: string, options: ParserOptions) =>
typescriptParsers.typescript.parse(text, { typescript: typescriptParsers.typescript }, options),
};

function transformParser(
parserName: 'babel' | 'typescript' | 'vue',
defaultParser: Parser,
): Parser {
return {
...defaultParser,
parse: (text: string, parsers: { [parserName: string]: Parser }, options: ParserOptions) => {
const firstFormattedText = format(text, {
...options,
plugins: [],
endOfLine: 'lf',
});
const ast = defaultParser.parse(firstFormattedText, { [parserName]: defaultParser }, options);

const classNameWrappedText = parseLineByLineAndReplace(
firstFormattedText,
ast,
// @ts-ignore
options,
format,
addon,
);

if (parserName === 'vue') {
return {
type: 'FormattedText',
body: classNameWrappedText,
};
}

const secondFormattedText = format(classNameWrappedText, {
...options,
plugins: [],
endOfLine: 'lf',
rangeEnd: Infinity,
});

return {
type: 'FormattedText',
body: secondFormattedText,
};
},
astFormat: 'classnames-ast',
};
}

export const parsers: { [parserName: string]: Parser } = {
babel: {
...babelParsers.babel,
astFormat: 'babel-ast',
},
typescript: {
...typescriptParsers.typescript,
astFormat: 'typescript-ast',
},
vue: {
...htmlParsers.vue,
astFormat: 'vue-ast',
},
babel: transformParser('babel', babelParsers.babel),
typescript: transformParser('typescript', typescriptParsers.typescript),
vue: transformParser('vue', htmlParsers.vue),
};
60 changes: 8 additions & 52 deletions src/packages/v2-plugin/printers.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,20 @@
import { parseLineByLineAndReplace } from 'core-parts';
import type { AstPath, ParserOptions, Doc, Printer, Parser } from 'prettier';
import { format } from 'prettier';
import { parsers as babelParsers } from 'prettier/parser-babel';
import { parsers as htmlParsers } from 'prettier/parser-html';
import { parsers as typescriptParsers } from 'prettier/parser-typescript';
import type { AstPath, ParserOptions, Doc, Printer } from 'prettier';

const addon = {
parseBabel: (text: string, options: ParserOptions) =>
babelParsers.babel.parse(text, { babel: babelParsers.babel }, options),
parseTypescript: (text: string, options: ParserOptions) =>
typescriptParsers.typescript.parse(text, { typescript: typescriptParsers.typescript }, options),
};

function createPrinter(parserName: 'babel' | 'typescript' | 'vue', defaultParser: Parser): Printer {
function createPrinter(): Printer {
function main(
path: AstPath,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options: ParserOptions,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
print: (path: AstPath) => Doc,
): Doc {
const comments = path.getValue()?.comments;

if (comments && Array.isArray(comments)) {
comments.forEach((comment: any) => {
// eslint-disable-next-line no-param-reassign
comment.printed = true;
});
}

const { originalText } = options;
const firstFormattedText = format(originalText, {
...options,
plugins: [],
endOfLine: 'lf',
});
const node = path.getValue();

const ast = defaultParser.parse(firstFormattedText, { [parserName]: defaultParser }, options);
const classNameWrappedText = parseLineByLineAndReplace(
firstFormattedText,
ast,
// @ts-ignore
options,
format,
addon,
);

if (parserName === 'vue') {
return classNameWrappedText;
if (node.type === 'FormattedText') {
return node.body;
}

const secondFormattedText = format(classNameWrappedText, {
...options,
plugins: [],
endOfLine: 'lf',
rangeEnd: Infinity,
});

return secondFormattedText;
throw new Error(`Unknown node type: ${node?.type}`);
}

return {
Expand All @@ -65,7 +23,5 @@ function createPrinter(parserName: 'babel' | 'typescript' | 'vue', defaultParser
}

export const printers: { [astFormat: string]: Printer } = {
'babel-ast': createPrinter('babel', babelParsers.babel),
'typescript-ast': createPrinter('typescript', typescriptParsers.typescript),
'vue-ast': createPrinter('vue', htmlParsers.vue),
'classnames-ast': createPrinter(),
};
1 change: 0 additions & 1 deletion src/packages/v3-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"author": "Hyeonjong <[email protected]>",
"license": "MIT",
"dependencies": {
"@prettier/sync": "0.3.0",
"core-parts": "workspace:*",
"prettier": "3.0.3"
}
Expand Down
Loading

0 comments on commit 409b956

Please sign in to comment.