Skip to content

Commit

Permalink
feat(prettier-plugin): create new @liferay/prettier-plugin package
Browse files Browse the repository at this point in the history
  • Loading branch information
bryceosterhaus committed May 10, 2024
1 parent 8656edb commit 726801e
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 0 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ module.exports = {
],
plugins: ['@liferay'],
rules: {
'@liferay/import-extensions': 'off',
'notice/notice': [
'error',
{
Expand Down
67 changes: 67 additions & 0 deletions .github/workflows/prettier-plugin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Based on: https://github.com/actions/starter-workflows/blob/master/ci/node.js.yml

name: prettier-plugin

on:
push:
branches: [master]
paths:
- 'projects/prettier-plugin/**'
pull_request:
branches: [master]
paths:
- 'projects/prettier-plugin/**'

env:
CI: true
yarn-cache-name: yarn-cache-3
yarn-cache-path: .yarn

jobs:
check-lockfile:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [20.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Use or update Yarn cache
uses: actions/cache@v2
with:
path: ${{ env.yarn-cache-path }}
key: ${{ runner.os }}-${{ env.yarn-cache-name }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn --cache-folder=${{ env.yarn-cache-path }}
working-directory: projects/prettier-plugin
- run: git diff --quiet -- yarn.lock
working-directory: projects/prettier-plugin

test:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [20.x]

steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Use or update Yarn cache
uses: actions/cache@v2
with:
path: ${{ env.yarn-cache-path }}
key: ${{ runner.os }}-${{ env.yarn-cache-name }}-${{ hashFiles('**/yarn.lock') }}-prettier-plugin
- run: yarn --cache-folder=../../../${{ env.yarn-cache-path }} --frozen-lockfile
working-directory: projects/prettier-plugin
- run: yarn --cache-folder=../../../${{ env.yarn-cache-path }} build
working-directory: projects/prettier-plugin
- run: yarn --cache-folder=../../../${{ env.yarn-cache-path }} test
working-directory: projects/prettier-plugin
7 changes: 7 additions & 0 deletions projects/prettier-plugin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: MIT
*/

export {parsers} from './parsers.js';
export {printers} from './printers.js';
17 changes: 17 additions & 0 deletions projects/prettier-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"dependencies": {
"prettier": "3.2.5"
},
"exports": "./index.js",
"name": "@liferay/prettier-plugin",
"scripts": {
"build": "true",
"format": "liferay-workspace-scripts format",
"format:check": "liferay-workspace-scripts format:check",
"postversion": "liferay-workspace-scripts publish",
"preversion": "liferay-workspace-scripts ci",
"test": "node test"
},
"type": "module",
"version": "1.0.0"
}
44 changes: 44 additions & 0 deletions projects/prettier-plugin/parsers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: MIT
*/

import {format} from 'prettier';
import {parsers as babelParsers} from 'prettier/plugins/babel';
import {parsers as typescriptParsers} from 'prettier/plugins/typescript';

import {linesAroundComments} from './rules/lines-around-comments.js';

function transformParser(parserName, defaultParser) {
return {
...defaultParser,
astFormat: 'liferay-style-ast',
parse: async (text, options) => {
/*
* We need to filter out our own plugin before calling default prettier
*/
const plugins = options.plugins.filter(
(plugin) => !plugin.printers['liferay-style-ast']
);

let formattedText = await format(text, {
...options,
plugins,
});

const ast = defaultParser.parse(formattedText, options);

formattedText = linesAroundComments(formattedText, ast, parserName);

return {
body: formattedText,
type: 'FormattedText',
};
},
};
}

export const parsers = {
babel: transformParser('babel', babelParsers.babel),
typescript: transformParser('typescript', typescriptParsers.typescript),
};
24 changes: 24 additions & 0 deletions projects/prettier-plugin/printers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: MIT
*/

function createPrinter() {
function main(path) {
const {node} = path;

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

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

return {
print: main,
};
}

export const printers = {
'liferay-style-ast': createPrinter(),
};
68 changes: 68 additions & 0 deletions projects/prettier-plugin/rules/lines-around-comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: MIT
*/

export function linesAroundComments(formattedText, ast, parserName) {
let contentLengthIncrease = 0;

ast.comments.forEach((commentNode) => {
if (isEndofLineComment(commentNode)) {
return;
}

const nodeStart =
parserName === 'typescript'
? commentNode.range[0]
: commentNode.start;
const nodeEnd =
parserName === 'typescript'
? commentNode.range[1]
: commentNode.end;

const commentStartIndex = nodeStart + contentLengthIncrease;

if (!isNewLineBefore(formattedText, commentStartIndex)) {
const position = commentStartIndex - 1;

formattedText = insertNewLine(formattedText, position);

contentLengthIncrease += 1;
}

const commentEndIndex = nodeEnd + contentLengthIncrease;

if (
!isBlockComment(commentNode) &&
!isNewLineAfter(formattedText, commentEndIndex)
) {
const position = commentEndIndex + 1;

formattedText = insertNewLine(formattedText, position);

contentLengthIncrease += 1;
}
});

return formattedText;
}

function isBlockComment(node) {
return node.type === 'CommentBlock' || node.type === 'Block';
}

function isEndofLineComment(node) {
return node.loc.start.column !== 0;
}

function isNewLineBefore(text, index) {
return text.charAt(index - 2) === '\n';
}

function isNewLineAfter(text, index) {
return text.charAt(index + 2) === '\n';
}

function insertNewLine(text, index) {
return [text.slice(0, index), '\n', text.slice(index)].join('');
}
92 changes: 92 additions & 0 deletions projects/prettier-plugin/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* SPDX-FileCopyrightText: © 2020 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: MIT
*/

import assert from 'node:assert';
import {describe, test} from 'node:test';
import {format} from 'prettier';

import * as liferayPrettierPlugin from '../index.js';

const baseConfig = {
bracketSpacing: false,
plugins: [liferayPrettierPlugin],
singleQuote: true,
tabWidth: 4,
useTabs: true,
};

const babelConfig = {
...baseConfig,
parser: 'babel',
};

const tsConfig = {
...baseConfig,
parser: 'typescript',
};

const fixtures = [
{
_name: 'inline comment',
code: `const foo = 'foo';
// test
const bar = 'bar';`,
expected: `const foo = 'foo';
// test
const bar = 'bar';
`,
},
{
_name: 'block comment',
code: `const foo = 'foo';
/*
* blah
*/
const bar = 'bar';`,
expected: `const foo = 'foo';
/*
* blah
*/
const bar = 'bar';
`,
},
];

describe('babel', () => {
test('prettier runs', async () => {
const code = `if (foo) {}`;

assert.ok(await format(code, babelConfig));
});

fixtures.forEach((fixture) => {
test(fixture._name, async () => {
assert.equal(
await format(fixture.code, babelConfig),
fixture.expected
);
});
});
});

describe('typescript', () => {
test('prettier runs', async () => {
const code = `if (foo) {}`;

assert.ok(await format(code, tsConfig));
});

fixtures.forEach((fixture) => {
test(fixture._name, async () => {
assert.equal(
await format(fixture.code, tsConfig),
fixture.expected
);
});
});
});

0 comments on commit 726801e

Please sign in to comment.