Skip to content

Commit

Permalink
Add Prettier (Azure#7637)
Browse files Browse the repository at this point in the history
  • Loading branch information
Phoenix He authored and yungezz committed Oct 29, 2019
1 parent 14543d3 commit bff55c4
Show file tree
Hide file tree
Showing 11 changed files with 410 additions and 163 deletions.
253 changes: 154 additions & 99 deletions .azure-pipelines-preproduction/package-lock.json

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions .azure-pipelines-preproduction/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
"@ts-common/iterator": "^0.3.6",
"@types/js-yaml": "^3.12.1",
"@types/mocha": "^5.2.6",
"cspell": "^4.0.12",
"@types/prettier": "^1.18.3",
"cspell": "^4.0.30",
"js-yaml": "^3.13.1",
"json-schema-ref-parser": "^6.1.0",
"mocha": "*",
"ts-node": "^8.1.0",
"tslib": "^1.10.0",
"typescript": "3.5.3"
"typescript": "3.5.3",
"prettier": "^1.18.2"
},
"homepage": "https://github.com/azure/azure-rest-api-specs",
"repository": {
Expand All @@ -36,6 +38,8 @@
"scripts": {
"test": "tsc && mocha -t 500000 --reporter min",
"spellcheck": "ts-node ./scripts/spellcheck.ts",
"prettier-check": "tsc && ts-node ./scripts/prettier-check.ts",
"prettier": "prettier",
"tsc": "tsc",
"multiapi": "ts-node ./scripts/multiapi.ts"
}
Expand Down
15 changes: 15 additions & 0 deletions .azure-pipelines/PrettierCheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
jobs:
- job: "PrettierCheck"
condition: "not(variables['PRIVATE'])"
variables:
NODE_OPTIONS: '--max-old-space-size=8192'
pool:
vmImage: 'Ubuntu 18.04'
continueOnError: true
steps:
- task: Npm@1
displayName: 'npm install'
inputs:
verbose: false
- script: 'npm run prettier-check'
displayName: 'Run Prettier Check'
5 changes: 5 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"plugins": ["./scripts/prettier-swagger-plugin"],
"parser": "json-swagger",
"printWidth": 20
}
12 changes: 12 additions & 0 deletions package-lock.json

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

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"description": "Tests for Azure REST API Specifications",
"license": "MIT",
"devDependencies": {
"@types/prettier": "^1.18.3",
"@azure/avocado": "^0.6.0",
"@azure/rest-api-specs-scripts": "^0.4.0",
"@ts-common/commonmark-to-markdown": "^1.2.0",
Expand All @@ -23,7 +24,8 @@
"mocha": "*",
"ts-node": "^8.1.0",
"tslib": "^1.10.0",
"typescript": "3.5.3"
"typescript": "3.5.3",
"prettier": "^1.18.2"
},
"homepage": "https://github.com/azure/azure-rest-api-specs",
"repository": {
Expand All @@ -37,6 +39,8 @@
"postinstall": "scripts/switch-to-preproduction.sh",
"test": "tsc && mocha -t 500000 --reporter min",
"spellcheck": "ts-node ./scripts/spellcheck.ts",
"prettier-check": "tsc && ts-node ./scripts/prettier-check.ts",
"prettier": "prettier",
"tsc": "tsc",
"multiapi": "ts-node ./scripts/multiapi.ts"
}
Expand Down
1 change: 1 addition & 0 deletions preproduction-azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ jobs:
- template: .azure-pipelines/BranchProtectionForPrivateRepo.yml
- template: .azure-pipelines/BreakingChange.yml
- template: .azure-pipelines/ModelValidation.yml
- template: .azure-pipelines/PrettierCheck.yml
22 changes: 22 additions & 0 deletions scripts/prettier-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { runCheckOverChangedSpecFiles, logWarn, logError } from "./utils";

runCheckOverChangedSpecFiles({
onCheckFile: (context, filePath) => {
return context.exec(`prettier -c ${filePath}`);
},

onExecError: async (result) => {
if (result.stdout) {
console.log(result.stdout);
}
},

onNotInCI: (context) => {
logWarn("Not in CI environment. Run against all the spec json.");
return context.exec(`prettier -c "specification/**/*.json"`);
},

onFinalFailed: async () => {
logError('Code style issues found in the above file(s). Please run `npm install && npm run prettier -- --write "specification/<service>/**/*.json"` to fix.')
}
})
92 changes: 92 additions & 0 deletions scripts/prettier-swagger-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { ParserOptions, FastPath, Doc, doc, Plugin, AST } from 'prettier';
import { parsers as bundledParsers } from 'prettier/parser-babylon';

const { concat, indent, hardline, join } = doc.builders;

// Modified from https://github.com/prettier/prettier/blob/master/src/language-js/printer-estree-json.js
const print = (path: FastPath, _: ParserOptions, print: (path: FastPath) => Doc): Doc => {
const node = path.getValue();
switch (node.type) {
case "JsonRoot":
return concat([path.call(print, "node"), hardline]);
case "ArrayExpression":
return node.elements.length === 0
? "[]"
: concat([
"[",
indent(
concat([
hardline,
join(concat([",", hardline]), path.map(print, "elements"))
])
),
hardline,
"]"
]);
case "ObjectExpression":
return node.properties.length === 0
? "{}"
: concat([
"{",
indent(
concat([
hardline,
join(concat([",", hardline]), path.map(print, "properties"))
])
),
hardline,
"}"
]);
case "ObjectProperty":
return concat([path.call(print, "key"), ": ", path.call(print, "value")]);
case "UnaryExpression":
return concat([
node.operator === "+" ? "" : node.operator,
path.call(print, "argument")
]);
case "NullLiteral":
return "null";
case "BooleanLiteral":
return node.value ? "true" : "false";
case "StringLiteral":
return JSON.stringify(node.value);
case "NumericLiteral":
// Modified: Keep numeric literal as-is
return node.extra.raw;
case "Identifier":
return JSON.stringify(node.name);
default:
/* istanbul ignore next */
throw new Error("unknown type: " + JSON.stringify(node.type));
}
}

const preprocess = (ast: AST, _: any): AST => {
return Object.assign({}, ast, {
type: "JsonRoot",
node: ast,
comments: []
});
}

export const languages: Plugin['languages'] = [
{
name: 'json-swagger',
extensions: ['.json'],
parsers: ['json-swagger']
}
]

export const parsers = {
'json-swagger': {
...bundledParsers['json-stringify'],
astFormat: 'estree-swagger-customized'
}
};

export const printers = {
'estree-swagger-customized': {
preprocess,
print
}
};
79 changes: 18 additions & 61 deletions scripts/spellcheck.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,25 @@
import { devOps, cli, childProcess } from '@azure/avocado';
import { ExecOptions } from 'child_process';
import { logError, logWarn, runCheckOverChangedSpecFiles } from './utils';

const logToAzureDevops = (msg: string, type: string) => {
const lines = msg.split('\n');
for (const line of lines) {
console.log(`##vso[task.logissue type=${type}]${line}`);
}
}

const logError = (msg: string) => logToAzureDevops(msg, 'error');
const logWarn = (msg: string) => logToAzureDevops(msg, 'warning');

const verboseExec = async (commandLine: string, options: ExecOptions = {}) => {
console.log(commandLine);
let result: any = {};
try {
result = await childProcess.exec(commandLine, options);
} catch (e) {
result = e;
}

if (!result.code) {
return 0;
}

if (result.stdout) {
logError(result.stdout);
}
if (result.stderr) {
console.error(result.stderr);
}
return result.code;
}
runCheckOverChangedSpecFiles({
onCheckFile: (context, filePath) => {
return context.exec(`cspell ${filePath}`);
},

const main = async () => {
const config = cli.defaultConfig();
const pr = await devOps.createPullRequestProperties(config);
if (pr === undefined) {
logWarn("Not in CI environment. Run against all the spec json.");
return verboseExec(`cspell "specification/**/*.json"`);
}

const changedJsonFiles = await pr.structuralDiff()
.filter(filePath => filePath.endsWith('.json') && filePath.startsWith('specification/'))
.toArray();
if (changedJsonFiles.length === 0) {
logWarn("No changed spec json file");
return 0;
}

let retCode = 0;
for (const jsonFile of changedJsonFiles) {
const code = await verboseExec(`cspell ${jsonFile}`);
if (code !== 0) {
retCode = code;
onExecError: async (result) => {
if (result.stdout) {
logError(result.stdout);
}
}
if (result.stderr) {
console.error(result.stderr);
}
},

return retCode;
}
onNotInCI: (context) => {
logWarn("Not in CI environment. Run against all the spec json.");
return context.exec(`cspell "specification/**/*.json"`);
},

main().then(retCode => {
if (retCode !== 0) {
onFinalFailed: async () => {
logError('Please fix the error or add words to ./custom-words.txt');
}
process.exit(retCode);
});
})
80 changes: 80 additions & 0 deletions scripts/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { ExecOptions } from 'child_process';
import { childProcess, cli, devOps } from '@azure/avocado';

export const logToAzureDevops = (msg: string, type: string) => {
const lines = msg.split('\n');
for (const line of lines) {
console.log(`##vso[task.logissue type=${type}]${line}`);
}
}

export const logError = (msg: string) => logToAzureDevops(msg, 'error');
export const logWarn = (msg: string) => logToAzureDevops(msg, 'warning');

export type Exec = (commandLine: string, options?: ExecOptions) => Promise<number>;
export type CheckContext = {
exec: Exec;
};
export type CheckOptions = {
onExecError(result: childProcess.ExecResult): Promise<unknown>;
onNotInCI(context: CheckContext): Promise<unknown>;
onCheckFile(context: CheckContext, filePath: string): Promise<number>;
onFinalFailed(context: CheckContext): Promise<unknown>;
}

const internalCheck = async (checkOptions: CheckOptions) => {

const exec = async (commandLine: string, options: ExecOptions = {}) => {
console.log(commandLine);
let result: any = {};
try {
result = await childProcess.exec(commandLine, options);
} catch (e) {
result = e;
}

if (!result.code) {
return 0;
}
await checkOptions.onExecError(result);

return result.code;
}
const context: CheckContext = { exec };

const config = cli.defaultConfig();
const pr = await devOps.createPullRequestProperties(config);
if (pr === undefined) {
return checkOptions.onNotInCI(context);
}

const changedJsonFiles = (await pr.diff())
.map(change => change.path)
.filter(filePath => filePath.endsWith('.json') && filePath.startsWith('specification/'));
if (changedJsonFiles.length === 0) {
logWarn("No changed spec json file");
return;
}

let retCode = 0;
for (const jsonFile of changedJsonFiles) {
const code = await checkOptions.onCheckFile(context, jsonFile);
if (code !== 0) {
retCode = code;
}
}

if (retCode !== 0) {
await checkOptions.onFinalFailed(context);
}

process.exit(retCode);
}

export const runCheckOverChangedSpecFiles = (options: CheckOptions) => {
internalCheck(options).catch(e => {
console.error(e);
logError(`Fatal Error. Please report to adxsr@microsoft.com`);
process.exit(-1);
});
}

0 comments on commit bff55c4

Please sign in to comment.