Toolkit: Remove deprecated plugin:build (#67485)

Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
This commit is contained in:
Esteban Beltran
2023-04-28 14:33:16 +02:00
committed by GitHub
co-authored by Jack Westbrook
parent ea7e5e2d82
commit 50fb1497e5
51 changed files with 111 additions and 4682 deletions
+5 -15
View File
@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { program } from 'commander';
import { pluginBuildTask } from './tasks/plugin.build';
import { getToolkitVersion } from './tasks/plugin.utils';
import { templateTask } from './tasks/template';
import { execTask } from './utils/execTask';
@@ -13,7 +12,7 @@ export const run = (includeInternalScripts = false) => {
program
.command('debug:template')
.description('Just testing')
.action(async (cmd) => {
.action(async () => {
await execTask(templateTask)({});
});
}
@@ -40,21 +39,12 @@ export const run = (includeInternalScripts = false) => {
.option('--skipTest', 'Skip running tests (for pipelines that run it separate)', false)
.option('--skipLint', 'Skip running lint (for pipelines that run it separate)', false)
.option('--preserveConsole', 'Preserves console calls', false)
.description('[Deprecated] Prepares plugin dist package')
.action(async (cmd) => {
console.log(chalk.yellow('\n⚠️ DEPRECATED. This command is deprecated and will be removed in v10. ⚠️'));
.description('[removed] Use grafana create-plugin instead')
.action(async () => {
console.log(
'Please migrate to grafana create-plugin https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin\n'
'No longer supported. Use grafana create-plugin https://github.com/grafana/plugin-tools/tree/main/packages/create-plugin\n'
);
await execTask(pluginBuildTask)({
coverage: cmd.coverage,
silent: true,
maxJestWorkers: cmd.maxJestWorkers,
preserveConsole: cmd.preserveConsole,
skipLint: cmd.skipLint,
skipTest: cmd.skipTest,
});
process.exit(1);
});
program
@@ -1,161 +0,0 @@
import { ESLint } from 'eslint';
import execa from 'execa';
import { constants as fsConstants, promises as fs } from 'fs';
import globby from 'globby';
import { resolve as resolvePath } from 'path';
import rimrafCallback from 'rimraf';
import { promisify } from 'util';
import { useSpinner } from '../utils/useSpinner';
import { bundlePlugin as bundleFn, PluginBundleOptions } from './plugin/bundle';
import { testPlugin } from './plugin/tests';
import { Task, TaskRunner } from './task';
const { access, copyFile } = fs;
const { COPYFILE_EXCL } = fsConstants;
const rimraf = promisify(rimrafCallback);
interface PluginBuildOptions {
coverage: boolean;
maxJestWorkers?: string;
preserveConsole?: boolean;
skipTest?: boolean;
skipLint?: boolean;
}
interface Fixable {
fix?: boolean;
}
const bundlePlugin = (options: PluginBundleOptions) => useSpinner('Compiling...', () => bundleFn(options));
// @ts-ignore
const clean = () => useSpinner('Cleaning', () => rimraf(`${process.cwd()}/dist`));
const copyIfNonExistent = (srcPath: string, destPath: string) =>
copyFile(srcPath, destPath, COPYFILE_EXCL)
.then(() => console.log(`Created: ${destPath}`))
.catch((error) => {
if (error.code !== 'EEXIST') {
throw error;
}
});
export const prepare = () =>
useSpinner('Preparing', () =>
Promise.all([
// Remove local dependencies for @grafana/data/node_modules
// See: https://github.com/grafana/grafana/issues/26748
rimraf(resolvePath(__dirname, 'node_modules/@grafana/data/node_modules')),
// Copy only if local tsconfig does not exist. Otherwise this will work, but have odd behavior
copyIfNonExistent(
resolvePath(__dirname, '../../config/tsconfig.plugin.local.json'),
resolvePath(process.cwd(), 'tsconfig.json')
),
// Copy only if local prettierrc does not exist. Otherwise this will work, but have odd behavior
copyIfNonExistent(
resolvePath(__dirname, '../../config/prettier.plugin.rc.js'),
resolvePath(process.cwd(), '.prettierrc.js')
),
])
);
export const versions = async () => {
try {
const nodeVersion = await execa('node', ['--version']);
console.log(`Using Node.js ${nodeVersion.stdout}`);
const toolkitVersion = await execa('grafana-toolkit', ['--version']);
console.log(`Using @grafana/toolkit ${toolkitVersion.stdout}`);
} catch (err) {
console.log(`Error reading versions`, err);
}
};
// @ts-ignore
const typecheckPlugin = () => useSpinner('Typechecking', () => execa('tsc', ['--noEmit']));
// @ts-ignore
const getStylesSources = () => globby(resolvePath(process.cwd(), 'src/**/*.+(scss|css)'));
export const lintPlugin = ({ fix }: Fixable = {}) =>
useSpinner('Linting', async () => {
try {
// Show a warning if the tslint file exists
await access(resolvePath(process.cwd(), 'tslint.json'));
console.log('\n');
console.log('--------------------------------------------------------------');
console.log('NOTE: @grafana/toolkit has migrated to use eslint');
console.log('Update your configs to use .eslintrc rather than tslint.json');
console.log('--------------------------------------------------------------');
} catch {
// OK: tslint does not exist
}
// @todo should remove this because the config file could be in a parent dir or within package.json
const configFile = await globby(resolvePath(process.cwd(), '.eslintrc?(.cjs|.js|.json|.yaml|.yml)')).then(
(filePaths) => {
if (filePaths.length > 0) {
return filePaths[0];
} else {
return resolvePath(__dirname, '../../config/eslint.plugin.js');
}
}
);
const eslint = new ESLint({
extensions: ['.ts', '.tsx'],
overrideConfigFile: configFile,
fix,
useEslintrc: false,
});
const results = await eslint.lintFiles(resolvePath(process.cwd(), 'src'));
if (fix) {
await ESLint.outputFixes(results);
}
const { errorCount, warningCount } = results.reduce<Record<string, number>>(
(acc, value) => {
acc.errorCount += value.errorCount;
acc.warningCount += value.warningCount;
return acc;
},
{ errorCount: 0, warningCount: 0 }
);
const formatter = await eslint.loadFormatter('stylish');
const resultText = formatter.format(results);
if (errorCount > 0 || warningCount > 0) {
console.log('\n');
console.log(resultText);
console.log('\n');
}
if (errorCount > 0) {
throw new Error(`${errorCount} linting errors found in ${results.length} files`);
}
});
export const pluginBuildRunner: TaskRunner<PluginBuildOptions> = async ({
coverage,
maxJestWorkers,
preserveConsole,
skipTest,
skipLint,
}) => {
await versions();
await prepare();
if (!skipLint) {
await lintPlugin({ fix: false });
}
if (!skipTest) {
await testPlugin({ updateSnapshot: false, coverage, maxWorkers: maxJestWorkers, watch: false });
}
await bundlePlugin({ watch: false, production: true, preserveConsole });
};
export const pluginBuildTask = new Task<PluginBuildOptions>('Build plugin', pluginBuildRunner);
@@ -1,79 +0,0 @@
import clearConsole from 'react-dev-utils/clearConsole';
import formatWebpackMessages from 'react-dev-utils/formatWebpackMessages';
import webpack from 'webpack';
import { loadWebpackConfig } from '../../../config/webpack.plugin.config';
export interface PluginBundleOptions {
watch: boolean;
production?: boolean;
preserveConsole?: boolean;
}
export const bundlePlugin = async ({ watch, production, preserveConsole }: PluginBundleOptions) => {
const compiler = webpack(
await loadWebpackConfig({
watch,
production,
preserveConsole,
})
);
const webpackPromise = new Promise<void>((resolve, reject) => {
if (watch) {
console.log('Started watching plugin for changes...');
compiler.watch({ ignored: ['**/node_modules', '**/dist'] }, (err, stats) => {});
compiler.hooks.invalid.tap('invalid', () => {
clearConsole();
console.log('Compiling...');
});
compiler.hooks.done.tap('done', (stats) => {
clearConsole();
const json = stats.toJson();
const output = formatWebpackMessages(json);
if (!output.errors.length && !output.warnings.length) {
console.log('Compiled successfully!\n');
console.log(stats.toString({ colors: true }));
}
if (output.errors.length) {
console.log('Compilation failed!');
output.errors.forEach((e) => console.log(e));
if (output.warnings.length) {
console.log('Warnings:');
output.warnings.forEach((w) => console.log(w));
}
}
if (output.errors.length === 0 && output.warnings.length) {
console.log('Compiled with warnings!');
output.warnings.forEach((w) => console.log(w));
}
});
} else {
compiler.run((err, stats) => {
if (err) {
reject(err);
return;
}
if (stats?.hasErrors()) {
stats.compilation.errors.forEach((e) => {
console.log(e.message);
});
reject('Build failed');
return;
}
console.log('\n', stats?.toString({ colors: true }), '\n');
resolve();
});
}
});
return webpackPromise;
};
@@ -1 +0,0 @@
NOTE: not a real image, but should be excluded from hashed files
@@ -1,50 +0,0 @@
import { runCLI } from '@jest/core';
import { loadJestPluginConfig } from '../../../config/jest.plugin.config';
import { useSpinner } from '../../utils/useSpinner';
export interface PluginTestOptions {
updateSnapshot: boolean;
coverage: boolean;
watch: boolean;
testPathPattern?: string;
testNamePattern?: string;
maxWorkers?: string;
}
export const testPlugin = ({
updateSnapshot,
coverage,
watch,
testPathPattern,
testNamePattern,
maxWorkers,
}: PluginTestOptions) =>
useSpinner('Running tests', async () => {
const testConfig = loadJestPluginConfig();
const cliConfig = {
config: JSON.stringify(testConfig),
updateSnapshot,
coverage,
watch,
testPathPattern: testPathPattern ? [testPathPattern] : [],
testNamePattern: testNamePattern ? [testNamePattern] : [],
passWithNoTests: true,
maxWorkers,
};
// @ts-ignore
const runJest = () => runCLI(cliConfig, [process.cwd()]);
if (watch) {
runJest();
} else {
// @ts-ignore
const results = await runJest();
if (results.results.numFailedTests > 0 || results.results.numFailedTestSuites > 0) {
throw new Error('Tests failed');
}
}
});
@@ -1,23 +0,0 @@
import ora from 'ora';
export const useSpinner = async (label: string, fn: () => Promise<any>, killProcess = true) => {
const spinner = ora(label);
spinner.start();
try {
await fn();
spinner.succeed();
} catch (err: any) {
spinner.fail(err.message || err);
if (err.stdout) {
console.error(err.stdout);
} else if (err.message) {
// Return stack trace if error object
console.trace(err); // eslint-disable-line no-console
}
if (killProcess) {
process.exit(1);
}
}
};