mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
@grafana/toolkit: cleanup (#21441)
* Clarify readme for extended webpack config * Add support for ES2019 features * Build task: path.resolve → resolvePath * Build task: avoid fs race conditions * Build task: use async fs functions * Build task: use rimraf directly ... because depending on the workspace parent to have it installed is fragile, and a child process is always slower. * Build task: misc
This commit is contained in:
parent
29e9b1f774
commit
f6130db03d
@ -128,21 +128,18 @@ Currently we support following Jest configuration properties:
|
||||
- [`moduleNameMapper`](https://jestjs.io/docs/en/configuration#modulenamemapper-object-string-string)
|
||||
|
||||
### How can I customize Webpack rules or plugins?
|
||||
You can provide your own webpack configuration.
|
||||
Provide a function implementing `CustomWebpackConfigurationGetter` in a file named `webpack.config.js`.
|
||||
|
||||
You can import the correct interface and Options from `@grafana/toolkit/src/config`.
|
||||
|
||||
Example
|
||||
You can provide your own `webpack.config.js` file that exports a `getWebpackConfig` function. We recommend that you extend the standard configuration, but you are free to create your own:
|
||||
|
||||
```js
|
||||
import CustomPlugin from 'custom-plugin';
|
||||
const CustomPlugin = require('custom-plugin');
|
||||
|
||||
export const getWebpackConfig = (defaultConfig, options) => {
|
||||
console.log('Custom config');
|
||||
defaultConfig.plugins.push(new CustomPlugin())
|
||||
return defaultConfig;
|
||||
};
|
||||
module.exports.getWebpackConfig = (config, options) => ({
|
||||
...config,
|
||||
plugins: [
|
||||
...config.plugins,
|
||||
new CustomPlugin()
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### How can I style my plugin?
|
||||
|
@ -37,8 +37,10 @@
|
||||
"@types/jest": "24.0.13",
|
||||
"@types/jest-cli": "^23.6.0",
|
||||
"@types/node": "^12.0.4",
|
||||
"@types/prettier": "^1.16.4",
|
||||
"@types/puppeteer-core": "1.9.0",
|
||||
"@types/react-dev-utils": "^9.0.1",
|
||||
"@types/rimraf": "^2.0.3",
|
||||
"@types/semver": "^6.0.0",
|
||||
"@types/tmp": "^0.1.0",
|
||||
"@types/webpack": "4.4.34",
|
||||
@ -55,7 +57,7 @@
|
||||
"execa": "^1.0.0",
|
||||
"expect-puppeteer": "4.1.1",
|
||||
"file-loader": "^4.0.0",
|
||||
"glob": "^7.1.4",
|
||||
"globby": "^10.0.1",
|
||||
"html-loader": "0.5.5",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"inquirer": "^6.3.1",
|
||||
@ -79,6 +81,7 @@
|
||||
"react-dev-utils": "^9.0.1",
|
||||
"replace-in-file": "^4.1.0",
|
||||
"replace-in-file-webpack-plugin": "^1.0.6",
|
||||
"rimraf": "^3.0.0",
|
||||
"sass-loader": "7.1.0",
|
||||
"semver": "^6.1.1",
|
||||
"simple-git": "^1.112.0",
|
||||
@ -94,10 +97,6 @@
|
||||
"url-loader": "^2.0.1",
|
||||
"webpack": "4.35.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/prettier": "^1.16.4"
|
||||
},
|
||||
"_moduleAliases": {
|
||||
"puppeteer": "node_modules/puppeteer-core"
|
||||
}
|
||||
|
@ -1,52 +1,56 @@
|
||||
import { Task, TaskRunner } from './task';
|
||||
import fs from 'fs';
|
||||
|
||||
// @ts-ignore
|
||||
import execa = require('execa');
|
||||
import path = require('path');
|
||||
import glob = require('glob');
|
||||
import { Linter, Configuration, RuleFailure } from 'tslint';
|
||||
import * as prettier from 'prettier';
|
||||
|
||||
import { useSpinner } from '../utils/useSpinner';
|
||||
import { testPlugin } from './plugin/tests';
|
||||
import { Task, TaskRunner } from './task';
|
||||
import rimrafCallback from 'rimraf';
|
||||
import { resolve as resolvePath } from 'path';
|
||||
import { promisify } from 'util';
|
||||
import globby from 'globby';
|
||||
import execa from 'execa';
|
||||
import { constants as fsConstants, promises as fs } from 'fs';
|
||||
import { bundlePlugin as bundleFn, PluginBundleOptions } from './plugin/bundle';
|
||||
import { Configuration, Linter, LintResult, RuleFailure } from 'tslint';
|
||||
|
||||
const { access, copyFile, readFile, writeFile } = fs;
|
||||
const { COPYFILE_EXCL, F_OK } = fsConstants;
|
||||
const rimraf = promisify(rimrafCallback);
|
||||
|
||||
interface PluginBuildOptions {
|
||||
coverage: boolean;
|
||||
}
|
||||
|
||||
interface Fixable {
|
||||
fix?: boolean;
|
||||
}
|
||||
|
||||
export const bundlePlugin = useSpinner<PluginBundleOptions>('Compiling...', async options => await bundleFn(options));
|
||||
|
||||
// @ts-ignore
|
||||
export const clean = useSpinner<void>('Cleaning', async () => await execa('rimraf', [`${process.cwd()}/dist`]));
|
||||
export const clean = useSpinner<void>('Cleaning', async () => await 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<void>('Preparing', async () => {
|
||||
// Make sure a local tsconfig exists. Otherwise this will work, but have odd behavior
|
||||
let filePath = path.resolve(process.cwd(), 'tsconfig.json');
|
||||
if (!fs.existsSync(filePath)) {
|
||||
const srcFile = path.resolve(__dirname, '../../config/tsconfig.plugin.local.json');
|
||||
fs.copyFile(srcFile, filePath, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
console.log(`Created: ${filePath}`);
|
||||
});
|
||||
}
|
||||
// Make sure a local .prettierrc.js exists. Otherwise this will work, but have odd behavior
|
||||
filePath = path.resolve(process.cwd(), '.prettierrc.js');
|
||||
if (!fs.existsSync(filePath)) {
|
||||
const srcFile = path.resolve(__dirname, '../../config/prettier.plugin.rc.js');
|
||||
fs.copyFile(srcFile, filePath, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
console.log(`Created: ${filePath}`);
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
await Promise.all([
|
||||
// Copy only if local tsconfig does not exist. Otherwise this will work, but have odd behavior
|
||||
copyIfNonExistent(
|
||||
resolvePath(process.cwd(), 'tsconfig.json'),
|
||||
resolvePath(__dirname, '../../config/tsconfig.plugin.local.json')
|
||||
),
|
||||
// Copy only if local prettierrc does not exist. Otherwise this will work, but have odd behavior
|
||||
copyIfNonExistent(
|
||||
resolvePath(process.cwd(), '.prettierrc.js'),
|
||||
resolvePath(__dirname, '../../config/prettier.plugin.rc.js')
|
||||
),
|
||||
]);
|
||||
|
||||
// Nothing is returned
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
@ -54,101 +58,97 @@ const typecheckPlugin = useSpinner<void>('Typechecking', async () => {
|
||||
await execa('tsc', ['--noEmit']);
|
||||
});
|
||||
|
||||
const getTypescriptSources = () => {
|
||||
const globPattern = path.resolve(process.cwd(), 'src/**/*.+(ts|tsx)');
|
||||
return glob.sync(globPattern);
|
||||
};
|
||||
const getTypescriptSources = () => globby(resolvePath(process.cwd(), 'src/**/*.+(ts|tsx)'));
|
||||
|
||||
const getStylesSources = () => {
|
||||
const globPattern = path.resolve(process.cwd(), 'src/**/*.+(scss|css)');
|
||||
return glob.sync(globPattern);
|
||||
};
|
||||
const getStylesSources = () => globby(resolvePath(process.cwd(), 'src/**/*.+(scss|css)'));
|
||||
|
||||
export const prettierCheckPlugin = useSpinner<Fixable>('Prettier check', async ({ fix }) => {
|
||||
const prettierConfig = require(path.resolve(__dirname, '../../config/prettier.plugin.config.json'));
|
||||
const sources = [...getStylesSources(), ...getTypescriptSources()];
|
||||
// @todo remove explicit params when possible -- https://github.com/microsoft/TypeScript/issues/35626
|
||||
const [prettierConfig, paths] = await Promise.all<object, string[]>([
|
||||
readFile(resolvePath(__dirname, '../../config/prettier.plugin.config.json'), 'utf8').then(
|
||||
contents => JSON.parse(contents) as object
|
||||
),
|
||||
|
||||
const promises = sources.map((s, i) => {
|
||||
return new Promise<{ path: string; failed: boolean }>((resolve, reject) => {
|
||||
fs.readFile(s, (err, data) => {
|
||||
let failed = false;
|
||||
if (err) {
|
||||
throw new Error(err.message);
|
||||
}
|
||||
Promise.all([getStylesSources(), getTypescriptSources()]).then(results => results.flat()),
|
||||
]);
|
||||
|
||||
const opts = {
|
||||
const promises: Array<Promise<{ path: string; success: boolean }>> = paths.map(path =>
|
||||
readFile(path, 'utf8')
|
||||
.then(contents => {
|
||||
const config = {
|
||||
...prettierConfig,
|
||||
filepath: s,
|
||||
filepath: path,
|
||||
};
|
||||
if (!prettier.check(data.toString(), opts)) {
|
||||
if (fix) {
|
||||
const fixed = prettier.format(data.toString(), opts);
|
||||
if (fixed && fixed.length > 10) {
|
||||
fs.writeFile(s, fixed, err => {
|
||||
if (err) {
|
||||
console.log('Error fixing ' + s, err);
|
||||
failed = true;
|
||||
} else {
|
||||
console.log('Fixed: ' + s);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('No automatic fix for: ' + s);
|
||||
failed = true;
|
||||
}
|
||||
} else {
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (fix && !prettier.check(contents, config)) {
|
||||
return prettier.format(contents, config);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
})
|
||||
.then(newContents => {
|
||||
if (fix && newContents && newContents.length > 10) {
|
||||
return writeFile(path, newContents)
|
||||
.then(() => {
|
||||
console.log(`Fixed: ${path}`);
|
||||
return true;
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(`Error fixing ${path}`, error);
|
||||
return false;
|
||||
});
|
||||
} else if (fix) {
|
||||
console.log(`No automatic fix for: ${path}`);
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.then(success => ({ path, success }))
|
||||
);
|
||||
|
||||
resolve({
|
||||
path: s,
|
||||
failed,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
const failures = (await Promise.all(promises)).filter(({ success }) => !success);
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
const failures = results.filter(r => r.failed);
|
||||
if (failures.length) {
|
||||
if (failures.length > 0) {
|
||||
console.log('\nFix Prettier issues in following files:');
|
||||
failures.forEach(f => console.log(f.path));
|
||||
failures.forEach(({ path }) => console.log(path));
|
||||
console.log('\nRun toolkit:dev to fix errors');
|
||||
throw new Error('Prettier failed');
|
||||
}
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
export const lintPlugin = useSpinner<Fixable>('Linting', async ({ fix }) => {
|
||||
let tsLintConfigPath = path.resolve(process.cwd(), 'tslint.json');
|
||||
if (!fs.existsSync(tsLintConfigPath)) {
|
||||
tsLintConfigPath = path.resolve(__dirname, '../../config/tslint.plugin.json');
|
||||
let tsLintConfigPath = resolvePath(process.cwd(), 'tslint.json');
|
||||
|
||||
try {
|
||||
await access(tsLintConfigPath, F_OK);
|
||||
} catch (error) {
|
||||
tsLintConfigPath = resolvePath(__dirname, '../../config/tslint.plugin.json');
|
||||
}
|
||||
|
||||
const options = {
|
||||
fix: fix === true,
|
||||
formatter: 'json',
|
||||
};
|
||||
|
||||
const configuration = Configuration.findConfiguration(tsLintConfigPath).results;
|
||||
const sourcesToLint = getTypescriptSources();
|
||||
const sourcesToLint = await getTypescriptSources();
|
||||
|
||||
const lintResults = sourcesToLint
|
||||
.map(fileName => {
|
||||
const lintPromises = sourcesToLint.map(fileName =>
|
||||
readFile(fileName, 'utf8').then(contents => {
|
||||
const linter = new Linter(options);
|
||||
const fileContents = fs.readFileSync(fileName, 'utf8');
|
||||
linter.lint(fileName, fileContents, configuration);
|
||||
linter.lint(fileName, contents, configuration);
|
||||
return linter.getResult();
|
||||
})
|
||||
.filter(result => {
|
||||
return result.errorCount > 0 || result.warningCount > 0;
|
||||
});
|
||||
);
|
||||
|
||||
const lintResults: LintResult[] = (await Promise.all(lintPromises)).filter(
|
||||
({ errorCount, warningCount }) => errorCount > 0 || warningCount > 0
|
||||
);
|
||||
|
||||
if (lintResults.length > 0) {
|
||||
console.log('\n');
|
||||
const failures = lintResults.reduce<RuleFailure[]>((failures, result) => {
|
||||
return [...failures, ...result.failures];
|
||||
}, []);
|
||||
const failures: RuleFailure[] = lintResults.flat();
|
||||
failures.forEach(f => {
|
||||
// tslint:disable-next-line
|
||||
console.log(
|
||||
|
@ -9,7 +9,6 @@
|
||||
"declaration": true,
|
||||
"declarationDir": "dist/src",
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"esModuleInterop": true,
|
||||
"lib": ["es2015", "es2017.string", "dom"]
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
"moduleResolution": "node",
|
||||
"outDir": "public/dist",
|
||||
"target": "es5",
|
||||
"lib": ["es6", "dom"],
|
||||
"lib": ["es2019", "dom"],
|
||||
"rootDirs": ["public/"],
|
||||
"jsx": "react",
|
||||
"module": "esnext",
|
||||
|
Loading…
Reference in New Issue
Block a user