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(js): add the setup-prettier generator #27996

Merged
merged 2 commits into from
Sep 24, 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
8 changes: 8 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -8185,6 +8185,14 @@
"children": [],
"isExternal": false,
"disableCollapsible": false
},
{
"id": "setup-prettier",
"path": "/nx-api/js/generators/setup-prettier",
"name": "setup-prettier",
"children": [],
"isExternal": false,
"disableCollapsible": false
}
],
"isExternal": false,
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/manifests/nx-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,15 @@
"originalFilePath": "/packages/js/src/generators/typescript-sync/schema.json",
"path": "/nx-api/js/generators/typescript-sync",
"type": "generator"
},
"/nx-api/js/generators/setup-prettier": {
"description": "Setup Prettier as the formatting tool.",
"file": "generated/packages/js/generators/setup-prettier.json",
"hidden": false,
"name": "setup-prettier",
"originalFilePath": "/packages/js/src/generators/setup-prettier/schema.json",
"path": "/nx-api/js/generators/setup-prettier",
"type": "generator"
}
},
"path": "/nx-api/js"
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/packages-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,15 @@
"originalFilePath": "/packages/js/src/generators/typescript-sync/schema.json",
"path": "js/generators/typescript-sync",
"type": "generator"
},
{
"description": "Setup Prettier as the formatting tool.",
"file": "generated/packages/js/generators/setup-prettier.json",
"hidden": false,
"name": "setup-prettier",
"originalFilePath": "/packages/js/src/generators/setup-prettier/schema.json",
"path": "js/generators/setup-prettier",
"type": "generator"
}
],
"githubRoot": "https://github.com/nrwl/nx/blob/master",
Expand Down
33 changes: 33 additions & 0 deletions docs/generated/packages/js/generators/setup-prettier.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "setup-prettier",
"factory": "./src/generators/setup-prettier/generator",
"schema": {
"$schema": "https://json-schema.org/schema",
"$id": "NxJsSetupPrettier",
"title": "Setup Prettier",
"description": "Setup Prettier as the formatting tool.",
"type": "object",
"properties": {
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false,
"x-priority": "internal"
},
"skipPackageJson": {
"description": "Do not add dependencies to `package.json`.",
"type": "boolean",
"default": false,
"x-priority": "internal"
}
},
"required": [],
"presets": []
},
"description": "Setup Prettier as the formatting tool.",
"implementation": "/packages/js/src/generators/setup-prettier/generator.ts",
"aliases": [],
"hidden": false,
"path": "/packages/js/src/generators/setup-prettier/schema.json",
"type": "generator"
}
1 change: 1 addition & 0 deletions docs/shared/reference/sitemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@
- [setup-verdaccio](/nx-api/js/generators/setup-verdaccio)
- [setup-build](/nx-api/js/generators/setup-build)
- [typescript-sync](/nx-api/js/generators/typescript-sync)
- [setup-prettier](/nx-api/js/generators/setup-prettier)
- [nest](/nx-api/nest)
- [documents](/nx-api/nest/documents)
- [Overview](/nx-api/nest/documents/overview)
Expand Down
5 changes: 5 additions & 0 deletions packages/js/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
"description": "Synchronize TypeScript project references based on the project graph",
"alias": ["sync"],
"hidden": true
},
"setup-prettier": {
"factory": "./src/generators/setup-prettier/generator",
"schema": "./src/generators/setup-prettier/schema.json",
"description": "Setup Prettier as the formatting tool."
}
}
}
72 changes: 14 additions & 58 deletions packages/js/src/generators/init/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import {
generateFiles,
GeneratorCallback,
readJson,
stripIndents,
runTasksInSerial,
Tree,
updateJson,
writeJson,
} from '@nx/devkit';
import { checkAndCleanWithSemver } from '@nx/devkit/src/utils/semver';
import { readModulePackageJson } from 'nx/src/utils/package-json';
import { join } from 'path';
import { satisfies, valid } from 'semver';
import { generatePrettierSetup } from '../../utils/prettier';
import { getRootTsConfigFileName } from '../../utils/typescript/ts-config';
import {
nxVersion,
Expand All @@ -24,7 +24,6 @@ import {
typescriptVersion,
} from '../../utils/versions';
import { InitSchema } from './schema';
import { join } from 'path';

async function getInstalledTypescriptVersion(
tree: Tree
Expand Down Expand Up @@ -105,53 +104,10 @@ export async function initGeneratorInternal(
}

if (schema.setUpPrettier) {
devDependencies['prettier'] = prettierVersion;

// https://prettier.io/docs/en/configuration.html
const prettierrcNameOptions = [
'.prettierrc',
'.prettierrc.json',
'.prettierrc.yml',
'.prettierrc.yaml',
'.prettierrc.json5',
'.prettierrc.js',
'.prettierrc.cjs',
'.prettierrc.mjs',
'.prettierrc.toml',
'prettier.config.js',
'prettier.config.cjs',
'prettier.config.mjs',
];

if (prettierrcNameOptions.every((name) => !tree.exists(name))) {
writeJson(tree, '.prettierrc', {
singleQuote: true,
});
}

if (!tree.exists(`.prettierignore`)) {
tree.write(
'.prettierignore',
stripIndents`
# Add files here to ignore them from prettier formatting
/dist
/coverage
/.nx/cache
/.nx/workspace-data
`
);
}
}

if (tree.exists('.vscode/extensions.json')) {
updateJson(tree, '.vscode/extensions.json', (json) => {
json.recommendations ??= [];
const extension = 'esbenp.prettier-vscode';
if (!json.recommendations.includes(extension)) {
json.recommendations.push(extension);
}
return json;
const prettierTask = generatePrettierSetup(tree, {
skipPackageJson: schema.skipPackageJson,
});
tasks.push(prettierTask);
}

const installTask = !schema.skipPackageJson
Expand All @@ -165,16 +121,16 @@ export async function initGeneratorInternal(
: () => {};
tasks.push(installTask);

if (schema.setUpPrettier) {
ensurePackage('prettier', prettierVersion);
if (!schema.skipFormat) await formatFiles(tree);
if (!schema.skipFormat) {
if (!schema.skipPackageJson) {
ensurePackage('prettier', prettierVersion);
}
// even if skipPackageJson === true, we can safely run formatFiles, prettier might
// have been installed earlier and if not, the formatFiles function still handles it
await formatFiles(tree);
}

return async () => {
for (const task of tasks) {
await task();
}
};
return runTasksInSerial(...tasks);
}

export default initGenerator;
86 changes: 86 additions & 0 deletions packages/js/src/generators/setup-prettier/generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { readJson, writeJson, type Tree } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { prettierVersion } from '../../utils/versions';
import { setupPrettierGenerator } from './generator';

describe('setup-prettier generator', () => {
let tree: Tree;

beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
// remove the default generated .prettierrc file
tree.delete('.prettierrc');
});

it('should install prettier package', async () => {
await setupPrettierGenerator(tree, { skipFormat: true });

const packageJson = readJson(tree, 'package.json');
expect(packageJson.devDependencies['prettier']).toBe(prettierVersion);
});

it('should create .prettierrc and .prettierignore files', async () => {
await setupPrettierGenerator(tree, { skipFormat: true });

const prettierrc = readJson(tree, '.prettierrc');
expect(prettierrc).toEqual({ singleQuote: true });
const prettierignore = tree.read('.prettierignore', 'utf-8');
expect(prettierignore).toMatch(/\n\/coverage/);
expect(prettierignore).toMatch(/\n\/dist/);
expect(prettierignore).toMatch(/\n\/\.nx\/cache/);
});

it('should not overwrite existing .prettierrc and .prettierignore files', async () => {
writeJson(tree, '.prettierrc', { singleQuote: false });
tree.write('.prettierignore', `# custom ignore file`);

await setupPrettierGenerator(tree, { skipFormat: true });

const prettierrc = readJson(tree, '.prettierrc');
expect(prettierrc).toEqual({ singleQuote: false });
const prettierignore = tree.read('.prettierignore', 'utf-8');
expect(prettierignore).toContain('# custom ignore file');
});

it('should not overwrite prettier configuration specified in other formats', async () => {
tree.delete('.prettierrc');
tree.delete('.prettierignore');
tree.write('.prettierrc.js', `module.exports = { singleQuote: true };`);

await setupPrettierGenerator(tree, { skipFormat: true });

expect(tree.exists('.prettierrc')).toBeFalsy();
expect(tree.exists('.prettierignore')).toBeTruthy();
expect(tree.read('.prettierrc.js', 'utf-8')).toContain(
`module.exports = { singleQuote: true };`
);
});

it('should add prettier vscode extension if .vscode/extensions.json file exists', async () => {
// No existing recommendations
writeJson(tree, '.vscode/extensions.json', {});

await setupPrettierGenerator(tree, { skipFormat: true });

let json = readJson(tree, '.vscode/extensions.json');
expect(json).toEqual({
recommendations: ['esbenp.prettier-vscode'],
});

// Existing recommendations
writeJson(tree, '.vscode/extensions.json', { recommendations: ['foo'] });

await setupPrettierGenerator(tree, { skipFormat: true });

json = readJson(tree, '.vscode/extensions.json');
expect(json).toEqual({
recommendations: ['foo', 'esbenp.prettier-vscode'],
});
});

it('should skip adding prettier extension if .vscode/extensions.json file does not exist', async () => {
await setupPrettierGenerator(tree, { skipFormat: true });

expect(tree.exists('.vscode/extensions.json')).toBeFalsy();
});
});
31 changes: 31 additions & 0 deletions packages/js/src/generators/setup-prettier/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
ensurePackage,
formatFiles,
type GeneratorCallback,
type Tree,
} from '@nx/devkit';
import { generatePrettierSetup } from '../../utils/prettier';
import { prettierVersion } from '../../utils/versions';
import type { GeneratorOptions } from './schema';

export async function setupPrettierGenerator(
tree: Tree,
options: GeneratorOptions
): Promise<GeneratorCallback> {
const prettierTask = generatePrettierSetup(tree, {
skipPackageJson: options.skipPackageJson,
});

if (!options.skipFormat) {
if (!options.skipPackageJson) {
ensurePackage('prettier', prettierVersion);
}
// even if skipPackageJson === true, we can safely run formatFiles, prettier might
// have been installed earlier and if not, the formatFiles function still handles it
await formatFiles(tree);
}

return prettierTask;
}

export default setupPrettierGenerator;
4 changes: 4 additions & 0 deletions packages/js/src/generators/setup-prettier/schema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface GeneratorOptions {
skipFormat?: boolean;
skipPackageJson?: boolean;
}
22 changes: 22 additions & 0 deletions packages/js/src/generators/setup-prettier/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://json-schema.org/schema",
"$id": "NxJsSetupPrettier",
"title": "Setup Prettier",
"description": "Setup Prettier as the formatting tool.",
"type": "object",
"properties": {
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false,
"x-priority": "internal"
},
"skipPackageJson": {
"description": "Do not add dependencies to `package.json`.",
"type": "boolean",
"default": false,
"x-priority": "internal"
}
},
"required": []
}
1 change: 1 addition & 0 deletions packages/js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './utils/package-json/update-package-json';
export * from './utils/package-json/create-entry-points';
export { libraryGenerator } from './generators/library/library';
export { initGenerator } from './generators/init/init';
export { setupPrettierGenerator } from './generators/setup-prettier/generator';
export { setupVerdaccio } from './generators/setup-verdaccio/generator';
export { isValidVariable } from './utils/is-valid-variable';

Expand Down
Loading