1import execa = require('execa');
2import { promises as fs } from 'fs';
3import * as path from 'path';
4import chalk from 'chalk';
5import { useSpinner } from '../utils/useSpinner';
6import { Task, TaskRunner } from './task';
7import { cloneDeep } from 'lodash';
8import globby from 'globby';
9
10const clean = (cwd: string) => useSpinner('Cleaning', () => execa('npm', ['run', 'clean'], { cwd }));
11
12const compile = (cwd: string) =>
13  useSpinner('Compiling sources', () => execa('tsc', ['-p', './tsconfig.build.json'], { cwd }));
14
15const bundle = (cwd: string) => useSpinner('Bundling', () => execa('npm', ['run', 'bundle'], { cwd }));
16
17const preparePackage = async (packageDist: string, pkg: any) => {
18  pkg = cloneDeep(pkg); // avoid mutations
19
20  pkg.main = 'index.js';
21  pkg.types = 'index.d.ts';
22
23  const version: string = pkg.version;
24  const name: string = pkg.name;
25  const deps: any = pkg.dependencies;
26
27  // Below we are adding cross-dependencies to Grafana's packages
28  // with the version being published
29  if (name.endsWith('/ui')) {
30    deps['@grafana/data'] = version;
31  } else if (name.endsWith('/runtime')) {
32    deps['@grafana/data'] = version;
33    deps['@grafana/ui'] = version;
34  } else if (name.endsWith('/toolkit')) {
35    deps['@grafana/data'] = version;
36    deps['@grafana/ui'] = version;
37  }
38
39  await useSpinner('Updating package.json', () =>
40    fs.writeFile(`${packageDist}/package.json`, JSON.stringify(pkg, null, 2))
41  );
42};
43
44const moveFiles = (fromPath: string, toPath: string) => {
45  const files = ['README.md', 'CHANGELOG.md', 'index.js'];
46
47  return useSpinner(`Moving ${files.join(', ')} files`, () => {
48    const promises = files.map((file) => fs.copyFile(`${fromPath}/${file}`, `${toPath}/${file}`));
49    return Promise.all(promises);
50  });
51};
52
53const moveStaticFiles = async (packageRoot: string, pkg: any) => {
54  if (pkg.name.endsWith('/ui')) {
55    return useSpinner('Moving static files', async () => {
56      const staticFiles = await globby(`${packageRoot}/src/**/*.{png,svg,gif,jpg}`);
57      const pathSearch = new RegExp(`^${packageRoot}/src`);
58      const pathReplace = `${packageRoot}/compiled`;
59      const promises = staticFiles.map((file) => fs.copyFile(file, file.replace(pathSearch, pathReplace)));
60      await Promise.all(promises);
61    });
62  }
63};
64
65interface PackageBuildOptions {
66  scope: string;
67}
68
69const buildTaskRunner: TaskRunner<PackageBuildOptions> = async ({ scope }) => {
70  if (!scope) {
71    throw new Error('Provide packages with -s, --scope <packages>');
72  }
73
74  const scopes = scope.split(',').map(async (s) => {
75    const packageRoot = path.resolve(__dirname, `../../../../grafana-${s}`);
76    const packageDist = `${packageRoot}/dist`;
77    const pkg = require(`${packageRoot}/package.json`);
78    console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`));
79    await clean(packageRoot);
80    await compile(packageRoot);
81    await moveStaticFiles(packageRoot, pkg);
82    await bundle(packageRoot);
83    await preparePackage(packageDist, pkg);
84    await moveFiles(packageRoot, packageDist);
85  });
86
87  await Promise.all(scopes);
88};
89
90export const buildPackageTask = new Task<PackageBuildOptions>('Package build', buildTaskRunner);
91