Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(prettier-plugin): create new @liferay/prettier-plugin package #1207

Merged
merged 1 commit into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ module.exports = {
'<rootDir>/projects/js-themes-toolkit',
'<rootDir>/projects/js-toolkit',
'<rootDir>/projects/npm-tools/packages/npm-scripts',
'<rootDir>/projects/prettier-plugin',

// Third-party.

Expand Down
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"
}
45 changes: 45 additions & 0 deletions projects/prettier-plugin/parsers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* 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
);
});
});
});
Loading