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

Tinypool #914

Closed
wants to merge 4 commits into from
Closed
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
66 changes: 29 additions & 37 deletions generators/bootstrap/support/eslint-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
*/
import { passthrough } from 'p-transform';
import { isFileStateModified } from 'mem-fs-editor/state';
import ESLint from 'eslint';
import { Minimatch } from 'minimatch';

import Tinypool from 'tinypool';
import BaseGenerator from '../../base-core/index.js';
import { getPackageRoot } from '../../../lib/index.js';
import { JS_PRETTIER_EXTENSIONS } from '../../generator-constants.js';
Expand All @@ -32,46 +32,38 @@ export const createESLintTransform = function (
) {
const { extensions = JS_PRETTIER_EXTENSIONS, ignoreErrors } = transformOptions;
const minimatch = new Minimatch(`**/*.{${extensions}}`, { dot: true });
const eslint = new ESLint.ESLint({
fix: true,
// Disable destination configs. We should apply plugins and rules which jhipster depends on.
useEslintrc: false,
resolvePluginsRelativeTo: getPackageRoot(),
overrideConfig: {
plugins: ['unused-imports', 'import'],
extends: ['plugin:@typescript-eslint/base'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 'latest',
},
rules: {
'import/order': 'error',
'import/no-duplicates': 'error',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': ['warn', { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' }],
},
},

const pool = new Tinypool({
runtime: 'child_process',
maxThreads: 1,
filename: new URL('./eslint-worker.js', import.meta.url).href,
});

return passthrough(async file => {
if (!minimatch.match(file.path) || !isFileStateModified(file)) {
return;
}
try {
if (await eslint.isPathIgnored(file.path)) {
return passthrough(
async file => {
if (!minimatch.match(file.path) || !isFileStateModified(file)) {
return;
}
const [result] = await eslint.lintText(file.contents.toString(), { filePath: file.path });
if (result.output) {
file.contents = Buffer.from(result.output);
}
} catch (error) {
if (ignoreErrors) {
this?.log?.warn?.(error);
return;
const fileContents = file.contents.toString();
const { result, error } = await pool.run({
resolvePluginsRelativeTo: getPackageRoot(),
filePath: file.path,
fileContents,
});
if (result) {
file.contents = Buffer.from(result);
}
if (error) {
const errorMessage = `Error parsing file ${file.relative}: ${error} at ${fileContents}`;
if (!ignoreErrors) {
throw new Error(errorMessage);
}

throw error;
}
});
this?.log?.warn?.(errorMessage);
}
},
() => {
pool.destroy();
},
);
};
38 changes: 38 additions & 0 deletions generators/bootstrap/support/eslint-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import ESLint from 'eslint';

let eslint;

export default async ({ resolvePluginsRelativeTo, filePath, fileContents }) => {
if (!eslint) {
eslint = new ESLint.ESLint({
fix: true,
// Disable destination configs. We should apply plugins and rules which jhipster depends on.
useEslintrc: false,
resolvePluginsRelativeTo,
overrideConfig: {
plugins: ['unused-imports', 'import'],
extends: ['plugin:@typescript-eslint/base'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 'latest',
},
rules: {
'import/order': 'error',
'import/no-duplicates': 'error',
'unused-imports/no-unused-imports': 'error',
'unused-imports/no-unused-vars': ['warn', { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' }],
},
},
});
}

if (await eslint.isPathIgnored(filePath)) {
return { result: fileContents };
}
try {
const [result] = await eslint.lintText(fileContents, { filePath });
return { result: result.output ?? fileContents };
} catch (error) {
return { error: `${error}` };
}
};
9 changes: 9 additions & 0 deletions generators/bootstrap/support/java-lint-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { removeUnusedImports } from 'java-lint';

export default async ({ fileContents }) => {
try {
return { result: await removeUnusedImports(fileContents) };
} catch (error) {
return { error: `${error}` };
}
};
48 changes: 33 additions & 15 deletions generators/bootstrap/support/java-unused-imports-transform.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { extname } from 'path';
import { passthrough } from '@yeoman/transform';
import { passthrough } from 'p-transform';
import { isFileStateModified } from 'mem-fs-editor/state';
import { removeUnusedImports } from 'java-lint';
import { VinylMemFsEditorFile } from 'mem-fs-editor';
import Tinypool from 'tinypool';
import CoreGenerator from '../../base-core/index.js';

// eslint-disable-next-line import/prefer-default-export
Expand All @@ -13,21 +13,39 @@ export const createRemoveUnusedImportsTransform = function (
} = {},
) {
const { ignoreErrors } = options;
return passthrough((file: VinylMemFsEditorFile) => {
if (extname(file.path) === '.java' && isFileStateModified(file)) {
if (file.contents) {
try {
file.contents = Buffer.from(removeUnusedImports(file.contents.toString('utf8')));
} catch (error: any) {
const errorMessage = `Error parsing file ${file.relative}: ${error} at ${file.contents?.toString()}`;
if (ignoreErrors) {
this?.log?.warn?.(errorMessage);
return;

const pool = new Tinypool({
runtime: 'child_process',
maxThreads: 1,
filename: new URL('./java-lint-worker.js', import.meta.url).href,
});

return passthrough(
async (file: VinylMemFsEditorFile) => {
if (extname(file.path) === '.java' && isFileStateModified(file)) {
if (file.contents) {
const fileContents = file.contents.toString('utf8');
const { result, error } = await pool.run({
fileContents,
fileRelativePath: file.relative,
});
if (result) {
file.contents = Buffer.from(result);
}
if (error) {
const errorMessage = `Error parsing file ${file.relative}: ${error} at ${fileContents}`;
if (ignoreErrors) {
this?.log?.warn?.(errorMessage);
return;
}

throw new Error(errorMessage);
throw new Error(errorMessage);
}
}
}
}
});
},
() => {
pool.destroy();
},
);
};
86 changes: 34 additions & 52 deletions generators/bootstrap/support/prettier-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@
*/
import { passthrough } from 'p-transform';
import { isFileStateModified } from 'mem-fs-editor/state';
import prettier from 'prettier';
import prettierPluginJava from 'prettier-plugin-java';
import prettierPluginProperties from 'prettier-plugin-properties';
import prettierPluginPackagejson from 'prettier-plugin-packagejson';
import type prettier from 'prettier';
import { Minimatch } from 'minimatch';
import type { MemFsEditorFile, VinylMemFsEditorFile } from 'mem-fs-editor';
import Tinypool from 'tinypool';
import type CoreGenerator from '../../base-core/index.js';

const minimatch = new Minimatch('**/{.prettierrc**,.prettierignore}');
Expand All @@ -41,62 +39,46 @@ export const createPrettierTransform = async function (
prettierOptions?: prettier.Options;
} = {},
) {
// prettier cache is global, generators may execute more than one commit.
// In case prettier config is committed to disk at later commits, the cache may be outdated.
await prettier.clearConfigCache();
const pool = new Tinypool({
runtime: 'child_process',
maxThreads: 1,
filename: new URL('./prettier-worker.js', import.meta.url).href,
});

const { ignoreErrors = false, extensions = '*', prettierPackageJson, prettierJava, prettierProperties, prettierOptions } = options;
const globExpression = extensions.includes(',') ? `**/*.{${extensions}}` : `**/*.${extensions}`;
const minimatch = new Minimatch(globExpression, { dot: true });

return passthrough(async (file: VinylMemFsEditorFile) => {
if (!minimatch.match(file.path) || !isFileStateModified(file)) {
return;
}
if (!file.contents) {
throw new Error(`File content doesn't exist for ${file.relative}`);
}
/* resolve from the projects config */
let fileContent;
try {
const resolvedDestinationFileOptions = await prettier.resolveConfig(file.relative);
const fileOptions: prettier.Options = {
// Config from disk
...resolvedDestinationFileOptions,
plugins: [],
// for better errors
filepath: file.relative,
...prettierOptions,
};
if (prettierPackageJson && file.path.endsWith('package.json')) {
fileOptions.plugins!.push(prettierPluginPackagejson);
}
if (prettierJava && file.path.endsWith('.java')) {
fileOptions.plugins!.push(prettierPluginJava);
return passthrough(
async (file: VinylMemFsEditorFile) => {
if (!minimatch.match(file.path) || !isFileStateModified(file)) {
return;
}
if (prettierProperties) {
fileOptions.plugins!.push(prettierPluginProperties);
if (!file.contents) {
throw new Error(`File content doesn't exist for ${file.relative}`);
}
fileContent = file.contents.toString('utf8');
const data = await prettier.format(fileContent, fileOptions);
file.contents = Buffer.from(data);
} catch (error) {
let errorMessage;
if (fileContent) {
errorMessage = `Error parsing file ${file.relative}: ${error}

At: ${fileContent
.split('\n')
.map((value, idx) => `${idx + 1}: ${value}`)
.join('\n')}`;
} else {
errorMessage = `Unknown prettier error: ${error}`;
const { result, errorMessage } = await pool.run({
relativeFilePath: file.relative,
filePath: file.path,
fileContents: file.contents.toString('utf8'),
prettierOptions,
prettierPackageJson,
prettierJava,
prettierProperties,
ignoreErrors,
});
if (result) {
file.contents = Buffer.from(result);
}
if (ignoreErrors) {
if (errorMessage) {
if (!ignoreErrors) {
throw new Error(errorMessage);
}
this?.log?.warn?.(errorMessage);
return;
}
throw new Error(errorMessage);
}
});
},
() => {
pool.destroy();
},
);
};
49 changes: 49 additions & 0 deletions generators/bootstrap/support/prettier-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import prettier from 'prettier';
import prettierPluginJava from 'prettier-plugin-java';
import prettierPluginProperties from 'prettier-plugin-properties';
import prettierPluginPackagejson from 'prettier-plugin-packagejson';

export default async ({
relativeFilePath,
filePath,
fileContents,
prettierOptions,
prettierPackageJson,
prettierJava,
prettierProperties,
}) => {
try {
const resolvedDestinationFileOptions = await prettier.resolveConfig(relativeFilePath);
const fileOptions = {
// Config from disk
...resolvedDestinationFileOptions,
plugins: [],
// for better errors
filepath: relativeFilePath,
...prettierOptions,
};
if (prettierPackageJson && filePath.endsWith('package.json')) {
fileOptions.plugins.push(prettierPluginPackagejson);
}
if (prettierJava && filePath.endsWith('.java')) {
fileOptions.plugins.push(prettierPluginJava);
}
if (prettierProperties) {
fileOptions.plugins.push(prettierPluginProperties);
}
return { result: await prettier.format(fileContents, fileOptions) };
} catch (error) {
let errorMessage;
if (fileContents) {
errorMessage = `Error parsing file ${relativeFilePath}: ${error}

At: ${fileContents
.split('\n')
.map((value, idx) => `${idx + 1}: ${value}`)
.join('\n')}`;
} else {
errorMessage = `Unknown prettier error: ${error}`;
}
return { errorMessage };
}
};
9 changes: 9 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@
"run-async": "3.0.0",
"semver": "7.6.0",
"simple-git": "3.22.0",
"tinypool": "0.8.2",
"type-fest": "4.10.2",
"uuid": "9.0.1",
"winston": "3.11.0",
Expand Down
Loading