@grafana/create-plugin 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.eslintrc +6 -0
  2. package/.github/workflows/create-release.yml +49 -0
  3. package/.github/workflows/npm-bump-version.yml +51 -0
  4. package/.github/workflows/test.yml +31 -0
  5. package/.prettierrc +10 -0
  6. package/CONTRIBUTING.md +42 -0
  7. package/README.md +143 -0
  8. package/dist/bin/run.js +17 -0
  9. package/dist/commands/generate.command.js +56 -0
  10. package/dist/commands/generate.plopfile.js +119 -0
  11. package/dist/commands/index.js +19 -0
  12. package/dist/commands/migrate.command.js +133 -0
  13. package/dist/commands/update.command.js +101 -0
  14. package/dist/constants.js +74 -0
  15. package/dist/utils/tests/utils.files.test.js +30 -0
  16. package/dist/utils/tests/utils.handlebars.test.js +26 -0
  17. package/dist/utils/tests/utils.npm.test.js +138 -0
  18. package/dist/utils/tests/utils.plugin.test.js +20 -0
  19. package/dist/utils/tests/utils.templates.test.js +32 -0
  20. package/dist/utils/utils.console.js +41 -0
  21. package/dist/utils/utils.files.js +61 -0
  22. package/dist/utils/utils.handlebars.js +73 -0
  23. package/dist/utils/utils.npm.js +162 -0
  24. package/dist/utils/utils.plugin.js +14 -0
  25. package/dist/utils/utils.templates.js +77 -0
  26. package/docs/example.mp4 +0 -0
  27. package/jest.config.js +20 -0
  28. package/package.json +69 -0
  29. package/src/bin/run.ts +15 -0
  30. package/src/commands/generate.command.ts +15 -0
  31. package/src/commands/generate.plopfile.ts +151 -0
  32. package/src/commands/index.ts +3 -0
  33. package/src/commands/migrate.command.ts +99 -0
  34. package/src/commands/update.command.ts +66 -0
  35. package/src/constants.ts +99 -0
  36. package/src/utils/tests/utils.files.test.ts +41 -0
  37. package/src/utils/tests/utils.handlebars.test.ts +35 -0
  38. package/src/utils/tests/utils.npm.test.ts +193 -0
  39. package/src/utils/tests/utils.plugin.test.ts +22 -0
  40. package/src/utils/tests/utils.templates.test.ts +44 -0
  41. package/src/utils/utils.console.ts +41 -0
  42. package/src/utils/utils.files.ts +65 -0
  43. package/src/utils/utils.handlebars.ts +47 -0
  44. package/src/utils/utils.npm.ts +189 -0
  45. package/src/utils/utils.plugin.ts +9 -0
  46. package/src/utils/utils.templates.ts +76 -0
  47. package/templates/_partials/backend-getting-started.md +20 -0
  48. package/templates/_partials/frontend-getting-started.md +59 -0
  49. package/templates/app/README.md +20 -0
  50. package/templates/app/src/components/App/App.test.tsx +32 -0
  51. package/templates/app/src/components/App/App.tsx +8 -0
  52. package/templates/app/src/components/App/index.tsx +1 -0
  53. package/templates/app/src/components/AppConfig/AppConfig.test.tsx +51 -0
  54. package/templates/app/src/components/AppConfig/AppConfig.tsx +92 -0
  55. package/templates/app/src/components/AppConfig/index.tsx +1 -0
  56. package/templates/app/src/module.ts +12 -0
  57. package/templates/app/src/plugin.json +33 -0
  58. package/templates/backend/Magefile.go +12 -0
  59. package/templates/backend/go.mod +5 -0
  60. package/templates/backend/go.sum +568 -0
  61. package/templates/backend/pkg/main.go +24 -0
  62. package/templates/backend/pkg/plugin/datasource.go +111 -0
  63. package/templates/backend/pkg/plugin/datasource_test.go +28 -0
  64. package/templates/common/.config/.eslintrc +12 -0
  65. package/templates/common/.config/.prettierrc.js +16 -0
  66. package/templates/common/.config/Dockerfile +15 -0
  67. package/templates/common/.config/README.md +115 -0
  68. package/templates/common/.config/jest-setup.js +24 -0
  69. package/templates/common/.config/jest.config.js +31 -0
  70. package/templates/common/.config/tsconfig.json +17 -0
  71. package/templates/common/.config/types/custom.d.ts +37 -0
  72. package/templates/common/.config/webpack/constants.ts +3 -0
  73. package/templates/common/.config/webpack/tsconfig.webpack.json +9 -0
  74. package/templates/common/.config/webpack/utils.ts +17 -0
  75. package/templates/common/.config/webpack/webpack.config.ts +187 -0
  76. package/templates/common/.eslintrc +3 -0
  77. package/templates/common/.nvmrc +1 -0
  78. package/templates/common/.prettierrc.js +4 -0
  79. package/templates/common/CHANGELOG.md +5 -0
  80. package/templates/common/LICENSE +201 -0
  81. package/templates/common/cypress/integration/01-smoke.spec.ts +10 -0
  82. package/templates/common/docker-compose.yaml +14 -0
  83. package/templates/common/jest-setup.js +2 -0
  84. package/templates/common/jest.config.js +4 -0
  85. package/templates/common/package.json +67 -0
  86. package/templates/common/src/README.md +5 -0
  87. package/templates/common/src/img/logo.svg +1 -0
  88. package/templates/common/tsconfig.json +3 -0
  89. package/templates/datasource/README.md +20 -0
  90. package/templates/datasource/src/components/ConfigEditor.tsx +83 -0
  91. package/templates/datasource/src/components/QueryEditor.tsx +50 -0
  92. package/templates/datasource/src/datasource.ts +46 -0
  93. package/templates/datasource/src/module.ts +9 -0
  94. package/templates/datasource/src/plugin.json +37 -0
  95. package/templates/datasource/src/types.ts +24 -0
  96. package/templates/github/ci/.github/workflows/ci.yml +76 -0
  97. package/templates/github/ci/.github/workflows/release.yml +165 -0
  98. package/templates/github/is-compatible/.github/workflows/is-compatible.yml +17 -0
  99. package/templates/panel/README.md +22 -0
  100. package/templates/panel/src/components/SimplePanel.tsx +62 -0
  101. package/templates/panel/src/module.test.ts +6 -0
  102. package/templates/panel/src/module.ts +40 -0
  103. package/templates/panel/src/plugin.json +33 -0
  104. package/templates/panel/src/types.ts +7 -0
  105. package/tsconfig.json +17 -0
@@ -0,0 +1,99 @@
1
+ import { TEXT, MIGRATION_CONFIG } from '../constants';
2
+ import { displayArrayAsList, printMessage, printSuccessMessage, confirmPrompt } from '../utils/utils.console';
3
+ import { compileTemplateFiles, getTemplateData } from '../utils/utils.templates';
4
+ import { getOnlyExistingInCwd, getOnlyNotExistingInCwd, removeFilesInCwd } from '../utils/utils.files';
5
+ import {
6
+ getPackageJsonUpdatesAsText,
7
+ updatePackageJson,
8
+ hasNpmDependenciesToUpdate,
9
+ getRemovableNpmDependencies,
10
+ removeNpmDependencies,
11
+ updateNpmScripts,
12
+ } from '../utils/utils.npm';
13
+
14
+ export const migrate = async () => {
15
+ try {
16
+ // 0. Warning
17
+ // -----------
18
+ printMessage(TEXT.migrationCommandWarning);
19
+
20
+ // 1. Add / update configuration files
21
+ // --------------------------
22
+ if (await confirmPrompt(TEXT.overrideFilesPrompt + '\n' + displayArrayAsList(MIGRATION_CONFIG.filesToOverride))) {
23
+ compileTemplateFiles(MIGRATION_CONFIG.filesToOverride, getTemplateData());
24
+ printSuccessMessage(TEXT.overrideFilesSuccess);
25
+ } else {
26
+ printMessage(TEXT.overrideFilesAborted);
27
+ process.exit(0);
28
+ }
29
+
30
+ // 2. Add necessary files if they don't exist (files we don't want to override)
31
+ // (skipped automatically if there are none)
32
+ // --------------------------------------------
33
+ const filesToExist = getOnlyNotExistingInCwd(MIGRATION_CONFIG.filesToExist);
34
+ if (filesToExist.length) {
35
+ if (await confirmPrompt(TEXT.filesToExistPrompt + '\n' + displayArrayAsList(filesToExist))) {
36
+ compileTemplateFiles(filesToExist, getTemplateData());
37
+ printSuccessMessage(TEXT.filesToExistSuccess);
38
+ } else {
39
+ printMessage(TEXT.filesToExistAborted);
40
+ }
41
+ }
42
+
43
+ // 3. Remove unnecessary files from the project
44
+ // (skipped automatically if there are none)
45
+ // --------------------------------------------
46
+ const filesToRemove = getOnlyExistingInCwd(MIGRATION_CONFIG.filesToRemove);
47
+ if (filesToRemove.length) {
48
+ if (await confirmPrompt(TEXT.removeFilesPrompt + '\n' + displayArrayAsList(filesToRemove))) {
49
+ removeFilesInCwd(filesToRemove);
50
+ printSuccessMessage(TEXT.removeFilesSuccess);
51
+ } else {
52
+ printMessage(TEXT.removeFilesAborted);
53
+ }
54
+ }
55
+
56
+ // 4. Add / update dependencies inside the `package.json`
57
+ // (skipped automatically if there is nothing to update)
58
+ // ------------------------------------------------
59
+ if (hasNpmDependenciesToUpdate()) {
60
+ if (await confirmPrompt(TEXT.updateNpmDependenciesPrompt + getPackageJsonUpdatesAsText())) {
61
+ updatePackageJson();
62
+ printSuccessMessage(TEXT.updateNpmDependenciesSuccess);
63
+ } else {
64
+ printMessage(TEXT.updateNpmDependenciesAborted);
65
+ }
66
+ }
67
+
68
+ // 5. Remove unncessary NPM dependencies from `package.json`
69
+ // (skipped automatically if there is nothing to be removed)
70
+ // ------------------------------------------------
71
+ const dependenciesToRemove = getRemovableNpmDependencies(MIGRATION_CONFIG.npmDependenciesToRemove);
72
+ const devDependenciesToRemove = getRemovableNpmDependencies(MIGRATION_CONFIG.devNpmDependenciesToRemove);
73
+ if (dependenciesToRemove.length) {
74
+ if (await confirmPrompt(TEXT.removeNpmDependenciesPrompt + '\n' + displayArrayAsList(dependenciesToRemove))) {
75
+ removeNpmDependencies(dependenciesToRemove);
76
+ removeNpmDependencies(devDependenciesToRemove, { devOnly: true });
77
+ printSuccessMessage(TEXT.removeNpmDependenciesSuccess);
78
+ } else {
79
+ printMessage(TEXT.removeNpmDependenciesAborted);
80
+ }
81
+ }
82
+
83
+ // 6. Add / update NPM scripts
84
+ // (skipped automatically if there is nothing to update)
85
+ // ------------------------------------------------
86
+ if (await confirmPrompt(TEXT.updateNpmScriptsPrompt)) {
87
+ updateNpmScripts();
88
+ printSuccessMessage(TEXT.updateNpmScriptsSuccess);
89
+ } else {
90
+ printMessage(TEXT.updateNpmScriptsAborted);
91
+ }
92
+
93
+ // 4. Summary
94
+ // -------------
95
+ printSuccessMessage(TEXT.migrationCommandSuccess);
96
+ } catch (error) {
97
+ console.error(error);
98
+ }
99
+ };
@@ -0,0 +1,66 @@
1
+ import { TEXT, UDPATE_CONFIG } from '../constants';
2
+ import { compileTemplateFiles, getTemplateData } from '../utils/utils.templates';
3
+ import { confirmPrompt, selectPrompt, printMessage, printSuccessMessage } from '../utils/utils.console';
4
+ import {
5
+ updatePackageJson,
6
+ hasNpmDependenciesToUpdate,
7
+ getPackageJsonUpdatesAsText,
8
+ updateNpmScripts,
9
+ } from '../utils/utils.npm';
10
+
11
+ export const update = async () => {
12
+ try {
13
+ // 0. Warning
14
+ // ----------
15
+ printMessage(TEXT.updateCommandWarning);
16
+
17
+ // 1. Add / update configuration files
18
+ // -----------------------------------
19
+ if (await confirmPrompt(TEXT.overrideFilesPrompt)) {
20
+ compileTemplateFiles(UDPATE_CONFIG.filesToOverride, getTemplateData());
21
+ printSuccessMessage(TEXT.overrideFilesSuccess);
22
+ } else {
23
+ printMessage(TEXT.overrideFilesAborted);
24
+ process.exit(0);
25
+ }
26
+
27
+ // 2. Add / update dependencies inside the `package.json`
28
+ // (skipped automatically if there is nothing to update)
29
+ // ------------------------------------------------
30
+ if (hasNpmDependenciesToUpdate()) {
31
+ const PROMPT_CHOICES = {
32
+ ALL: 'Yes, all of them',
33
+ ONLY_OUTDATED: 'Yes, but only the outdated ones',
34
+ NONE: 'No',
35
+ };
36
+ const shouldUpdateDeps = await selectPrompt(TEXT.updateNpmDependenciesPrompt + getPackageJsonUpdatesAsText(), [
37
+ PROMPT_CHOICES.ALL,
38
+ PROMPT_CHOICES.ONLY_OUTDATED,
39
+ PROMPT_CHOICES.NONE,
40
+ ]);
41
+
42
+ if (shouldUpdateDeps && shouldUpdateDeps !== PROMPT_CHOICES.NONE) {
43
+ updatePackageJson({ onlyOutdated: shouldUpdateDeps === PROMPT_CHOICES.ONLY_OUTDATED });
44
+ printSuccessMessage(TEXT.updateNpmDependenciesSuccess);
45
+ } else {
46
+ printMessage(TEXT.updateNpmDependenciesAborted);
47
+ }
48
+ }
49
+
50
+ // 3. Add / update NPM scripts
51
+ // (skipped automatically if there is nothing to update)
52
+ // ------------------------------------------------
53
+ if (await confirmPrompt(TEXT.updateNpmScriptsPrompt)) {
54
+ updateNpmScripts();
55
+ printSuccessMessage(TEXT.updateNpmScriptsSuccess);
56
+ } else {
57
+ printMessage(TEXT.updateNpmScriptsAborted);
58
+ }
59
+
60
+ // 4. Summary
61
+ // -------------
62
+ printSuccessMessage(TEXT.updateCommandSuccess);
63
+ } catch (error) {
64
+ console.error(error);
65
+ }
66
+ };
@@ -0,0 +1,99 @@
1
+ import path from 'path';
2
+
3
+ export const IS_DEV = process.env.CREATE_PLUGIN_DEV !== undefined;
4
+
5
+ export const DEV_EXPORT_DIR = path.join(__dirname, '..', 'generated');
6
+
7
+ export const EXPORT_PATH_PREFIX = IS_DEV ? DEV_EXPORT_DIR : process.cwd();
8
+
9
+ export const DIST_DIR = path.join(__dirname, 'dist');
10
+
11
+ export const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
12
+
13
+ // Partials are template files that can be used in other templates
14
+ // (they won't be copied over to the generated folder as they are)
15
+ export const PARTIALS_DIR = path.join(TEMPLATES_DIR, '_partials');
16
+
17
+ export const TEMPLATE_PATHS: Record<string, string> = {
18
+ app: path.join(TEMPLATES_DIR, 'app'),
19
+ backend: path.join(TEMPLATES_DIR, 'backend'),
20
+ common: path.join(TEMPLATES_DIR, 'common'),
21
+ datasource: path.join(TEMPLATES_DIR, 'datasource'),
22
+ panel: path.join(TEMPLATES_DIR, 'panel'),
23
+ ciWorkflows: path.join(TEMPLATES_DIR, 'github', 'ci'),
24
+ isCompatibleWorkflow: path.join(TEMPLATES_DIR, 'github', 'is-compatible'),
25
+ };
26
+
27
+ export enum PLUGIN_TYPES {
28
+ app = 'app',
29
+ panel = 'panel',
30
+ datasource = 'datasource',
31
+ }
32
+
33
+ // This gets merged into variables coming from user prompts (when using Plop) or any other dynamic variables,
34
+ // and will be available to use in the templates.
35
+ // Example: "@grafana/ui": "{{ grafanaVersion }}"
36
+ export const EXTRA_TEMPLATE_VARIABLES = {
37
+ grafanaVersion: '9.1.2',
38
+ };
39
+
40
+ export const MIGRATION_CONFIG = {
41
+ // Files that should be overriden during a migration.
42
+ // (paths are relative to the scaffolded projects root)
43
+ filesToOverride: [
44
+ '.config/',
45
+ '.eslintrc',
46
+ '.nvmrc',
47
+ '.prettierrc.js',
48
+ 'docker-compose.yaml', // Using .yaml instead of .yml (https://yaml.org/faq.html)
49
+ 'jest-setup.js',
50
+ 'jest.config.js',
51
+ 'tsconfig.json',
52
+ ],
53
+ // Files that are mandatory for the plugins, but we don't want to ever override them. We are only creating the ones that don't exist.
54
+ // (paths are relative to the scaffolded projects root)
55
+ filesToExist: ['CHANGELOG.md'],
56
+ // Files that are no longer needed for the project and possibly can be removed.
57
+ filesToRemove: ['Dockerfile', 'docker-compose.yml', 'webpack/', '.webpack/', '.prettierrc'],
58
+ // NPM dependencies that are no longer needed for the project and possibly can be removed.
59
+ npmDependenciesToRemove: ['ts-loader', 'babel-loader', '@grafana/toolkit'],
60
+ devNpmDependenciesToRemove: ['@grafana/toolkit', '@grafana/runtime', '@grafana/data', '@grafana/ui'],
61
+ };
62
+
63
+ export const UDPATE_CONFIG = {
64
+ // Files that should be overriden between configuration version updates.
65
+ filesToOverride: ['.config/'],
66
+ };
67
+
68
+ // prettier-ignore
69
+ export const TEXT = {
70
+ overrideFilesPrompt: '**The following files will be overriden, would you like to continue?**',
71
+ overrideFilesSuccess: 'Configuration files updated successfully.',
72
+ overrideFilesAborted: 'Migration aborted.',
73
+
74
+ filesToExistPrompt: '**The following files are necessary for the project to work, can we scaffold them for you?**',
75
+ filesToExistSuccess: 'Extra necessaryily files created successfully.',
76
+ filesToExistAborted: 'No extra necessary files were scaffolded.',
77
+
78
+ removeFilesPrompt: '**The following files are possibly not needed for the project anymore, are you ok with us removing them?**',
79
+ removeFilesSuccess: 'Unnecessary files have been removed successfully.',
80
+ removeFilesAborted: 'No unnecessary files were deleted.',
81
+
82
+ updateNpmDependenciesPrompt: '**Would you like to update the following dependencies in the `package.json?`**',
83
+ updateNpmDependenciesSuccess: 'Successfully updated the NPM dependencies.',
84
+ updateNpmDependenciesAborted: 'No NPM dependencies have been updated.',
85
+
86
+ removeNpmDependenciesPrompt: '**Would you like to remove the following possibly unnecessary NPM dependencies?**',
87
+ removeNpmDependenciesSuccess: 'Unnecessary NPM dependencies removed successfully.',
88
+ removeNpmDependenciesAborted: 'No NPM dependencies have been removed.',
89
+
90
+ updateNpmScriptsPrompt: '**Would you like to update the `{ scripts }` in your `package.json`?**',
91
+ updateNpmScriptsSuccess: 'NPM scripts updated successfully.',
92
+ updateNpmScriptsAborted: 'No NPM scripts have been added or updated.',
93
+
94
+ migrationCommandWarning: '**⚠️ Warning!**\nThis is going to change files in your current project to make it up-to-date with the latest plugin configuration.\nPlease make sure that you have backed up your changes.',
95
+ migrationCommandSuccess: '**Done.**\nIf you have any questions please open an issue/discussion in https://github.com/grafana/create-plugin.',
96
+
97
+ updateCommandWarning: '**⚠️ Warning!**\nThis is going to update files under the `.config/` folder.\nMake sure to commit your changes before running this script.',
98
+ updateCommandSuccess: '**Done.**\nIf you have any questions please open an issue/discussion in https://github.com/grafana/create-plugin.',
99
+ }
@@ -0,0 +1,41 @@
1
+ import { PLUGIN_TYPES, TEMPLATE_PATHS } from '../../constants';
2
+ import { filterOutCommonFiles, isFile } from '../utils.files';
3
+
4
+ describe('Utils/Files', () => {
5
+ describe('filterOutCommonFiles()', () => {
6
+ test('should remove common template files from the list that have a plugin-specific override', () => {
7
+ expect(
8
+ filterOutCommonFiles(
9
+ [
10
+ // Common template files
11
+ `${TEMPLATE_PATHS.common}/.config/.eslintrc`,
12
+ `${TEMPLATE_PATHS.common}/.config/.gitignore`,
13
+ `${TEMPLATE_PATHS.common}/.config/package.json`,
14
+
15
+ // Override of a common template file
16
+ `${TEMPLATE_PATHS.app}/.config/package.json`,
17
+
18
+ // Only exists in the plugin-type specific templates
19
+ `${TEMPLATE_PATHS.app}/.config/README.md`,
20
+ ],
21
+ PLUGIN_TYPES.app
22
+ )
23
+ ).toEqual([
24
+ `${TEMPLATE_PATHS.common}/.config/.eslintrc`,
25
+ `${TEMPLATE_PATHS.common}/.config/.gitignore`,
26
+ `${TEMPLATE_PATHS.app}/.config/package.json`,
27
+ `${TEMPLATE_PATHS.app}/.config/README.md`,
28
+ ]);
29
+ });
30
+ });
31
+
32
+ describe('isFile()', () => {
33
+ test('should return TRUE if the path is pointing to a file', () => {
34
+ expect(isFile(`${TEMPLATE_PATHS.common}/LICENSE`)).toBe(true);
35
+ });
36
+
37
+ test('should return FALSE if the path is pointing to a directory', () => {
38
+ expect(isFile(TEMPLATE_PATHS.common)).toBe(false);
39
+ });
40
+ });
41
+ });
@@ -0,0 +1,35 @@
1
+ import { PLUGIN_TYPES } from '../../constants';
2
+ import { normalizeId } from '../utils.handlebars';
3
+
4
+ describe('Handlebars helpers', () => {
5
+ describe('normalize id', () => {
6
+ test.each([PLUGIN_TYPES.app, PLUGIN_TYPES.datasource, PLUGIN_TYPES.panel])(
7
+ 'should return the id with the type appended',
8
+ (type: PLUGIN_TYPES) => {
9
+ const pluginName = 'my-plugin';
10
+ const orgName = 'my-org';
11
+ const actual = normalizeId(pluginName, orgName, type);
12
+ expect(actual).toEqual(`my-org-my-plugin-${type}`);
13
+ }
14
+ );
15
+ test.each([PLUGIN_TYPES.app, PLUGIN_TYPES.datasource, PLUGIN_TYPES.panel])(
16
+ 'should not duplicate the type if it is already present',
17
+ (type: PLUGIN_TYPES) => {
18
+ const pluginName = `my-plugin-${type}`;
19
+ const orgName = 'my-org';
20
+ const actual = normalizeId(pluginName, orgName, type);
21
+ expect(actual).toEqual(`my-org-my-plugin-${type}`);
22
+ }
23
+ );
24
+
25
+ test.each([PLUGIN_TYPES.app, PLUGIN_TYPES.datasource, PLUGIN_TYPES.panel])(
26
+ 'should not duplicate the type if it is already present (no dash before type)',
27
+ (type: PLUGIN_TYPES) => {
28
+ const pluginName = `my-plugin${type}`;
29
+ const orgName = 'my-org';
30
+ const actual = normalizeId(pluginName, orgName, type);
31
+ expect(actual).toEqual(`my-org-my-plugin-${type}`);
32
+ }
33
+ );
34
+ });
35
+ });
@@ -0,0 +1,193 @@
1
+ import { updateNpmDependencies, getUpdatableNpmDependencies } from '../utils.npm';
2
+
3
+ describe('Utils / NPM', () => {
4
+ describe('getDepenciesToBeUpdated()', () => {
5
+ test('it should return a summary of dependencies to be updated', () => {
6
+ expect(
7
+ getUpdatableNpmDependencies(
8
+ // current
9
+ {
10
+ '@grafana/tsconfig': '^1.2.0-rc1',
11
+ '@swc/core': '^1.2.162',
12
+ '@types/minimist': '^1.6.2',
13
+ '@types/mkdirp': '^1.0.2',
14
+ '@types/semver': '^7.3.9',
15
+ 'node-plop': '0.26.3',
16
+ },
17
+ // new
18
+ {
19
+ '@grafana/tsconfig': '^2.0.0', // updated
20
+ '@swc/core': '^1.2.162',
21
+ '@types/minimist': '^1.4.2', // updated
22
+ '@types/mkdirp': '^1.0.2',
23
+ '@types/semver': '^7.3.9',
24
+ }
25
+ )
26
+ ).toEqual({
27
+ '@grafana/tsconfig': { prev: '^1.2.0-rc1', next: '^2.0.0' },
28
+ '@types/minimist': { prev: '^1.6.2', next: '^1.4.2' },
29
+ });
30
+ });
31
+
32
+ test('it should be possible to only update dependencies that are outdated', () => {
33
+ expect(
34
+ getUpdatableNpmDependencies(
35
+ // current
36
+ {
37
+ '@grafana/tsconfig': '^1.2.0-rc1',
38
+ '@swc/core': '^1.2.162',
39
+ '@types/minimist': '^1.6.2',
40
+ '@types/mkdirp': '^1.0.2',
41
+ '@types/semver': '^7.3.9',
42
+ 'node-plop': '0.26.3',
43
+ },
44
+ // new
45
+ {
46
+ '@grafana/tsconfig': '^2.0.0', // updated
47
+ '@swc/core': '^1.2.162',
48
+ '@types/minimist': '^1.4.2', // updated - BUT older than the current one
49
+ '@types/mkdirp': '^1.0.2',
50
+ '@types/semver': '^7.3.9',
51
+ },
52
+ { onlyOutdated: true }
53
+ )
54
+ ).toEqual({
55
+ '@grafana/tsconfig': { prev: '^1.2.0-rc1', next: '^2.0.0' },
56
+ });
57
+ });
58
+
59
+ test('it should be able to override with using release channels like "latest"', () => {
60
+ expect(
61
+ getUpdatableNpmDependencies(
62
+ // current
63
+ {
64
+ '@grafana/tsconfig': 'latest',
65
+ '@swc/core': '^1.2.162',
66
+ '@types/minimist': '^1.6.2',
67
+ '@types/mkdirp': '^1.0.2',
68
+ '@types/semver': '^7.3.9',
69
+ 'node-plop': '0.26.3',
70
+ },
71
+ // new
72
+ {
73
+ '@grafana/tsconfig': '^2.0.0', // updated
74
+ '@swc/core': 'latest', // updated
75
+ '@types/minimist': '^1.4.2', // updated
76
+ '@types/mkdirp': '^1.0.2',
77
+ '@types/semver': '^7.3.9',
78
+ }
79
+ )
80
+ ).toEqual({
81
+ '@grafana/tsconfig': { prev: 'latest', next: '^2.0.0' },
82
+ '@swc/core': { prev: '^1.2.162', next: 'latest' },
83
+ '@types/minimist': { prev: '^1.6.2', next: '^1.4.2' },
84
+ });
85
+ });
86
+ });
87
+
88
+ describe('updateDependencies()', () => {
89
+ test('it should update existing dependencies', () => {
90
+ expect(
91
+ updateNpmDependencies(
92
+ // dependencies
93
+ {
94
+ '@grafana/tsconfig': '^1.2.0-rc1',
95
+ '@swc/core': '^1.2.162',
96
+ '@types/minimist': '^1.2.2',
97
+ '@types/mkdirp': '^1.0.2',
98
+ '@types/semver': '^7.3.9',
99
+ 'node-plop': '0.26.3',
100
+ },
101
+ // update summary
102
+ {
103
+ '@grafana/tsconfig': { prev: '^1.2.0-rc1', next: '^2.0.0' },
104
+ '@types/minimist': { prev: '^1.2.2', next: '^1.4.2' },
105
+ }
106
+ )
107
+ ).toEqual({
108
+ '@grafana/tsconfig': '^2.0.0', // updated
109
+ '@swc/core': '^1.2.162',
110
+ '@types/minimist': '^1.4.2', // updated
111
+ '@types/mkdirp': '^1.0.2',
112
+ '@types/semver': '^7.3.9',
113
+ 'node-plop': '0.26.3',
114
+ });
115
+ });
116
+
117
+ test('it should add new depdendencies', () => {
118
+ expect(
119
+ updateNpmDependencies(
120
+ // dependencies
121
+ {
122
+ '@grafana/tsconfig': '^1.2.0-rc1',
123
+ '@swc/core': '^1.2.162',
124
+ },
125
+ // update summary
126
+ {
127
+ '@grafana/tsconfig': { prev: '^1.2.0-rc1', next: '^1.2.0-rc1' },
128
+ '@swc/core': { prev: '^1.2.162', next: '^1.2.162' },
129
+ '@types/minimist': { prev: null, next: '^1.4.2' }, // new
130
+ }
131
+ )
132
+ ).toEqual({
133
+ '@grafana/tsconfig': '^1.2.0-rc1',
134
+ '@swc/core': '^1.2.162',
135
+ '@types/minimist': '^1.4.2', // new
136
+ });
137
+ });
138
+
139
+ test('it should override `latest` and other release channels', () => {
140
+ expect(
141
+ updateNpmDependencies(
142
+ // dependencies
143
+ {
144
+ '@grafana/tsconfig': 'latest',
145
+ '@swc/core': '^1.2.162',
146
+ '@types/minimist': '^1.2.2',
147
+ '@types/mkdirp': '^1.0.2',
148
+ '@types/semver': '^8.0.0',
149
+ 'node-plop': '0.26.3',
150
+ },
151
+ // update summary
152
+ {
153
+ '@grafana/tsconfig': { prev: 'latest', next: '^2.0.0' },
154
+ }
155
+ )
156
+ ).toEqual({
157
+ '@grafana/tsconfig': '^2.0.0', // updated
158
+ '@swc/core': '^1.2.162',
159
+ '@types/minimist': '^1.2.2',
160
+ '@types/mkdirp': '^1.0.2',
161
+ '@types/semver': '^8.0.0',
162
+ 'node-plop': '0.26.3',
163
+ });
164
+ });
165
+
166
+ test('it should be able to override with using release channels like "latest"', () => {
167
+ expect(
168
+ updateNpmDependencies(
169
+ // dependencies
170
+ {
171
+ '@grafana/tsconfig': '^2.0.0',
172
+ '@swc/core': '^1.2.162',
173
+ '@types/minimist': '^1.2.2',
174
+ '@types/mkdirp': '^1.0.2',
175
+ '@types/semver': '^8.0.0',
176
+ 'node-plop': '0.26.3',
177
+ },
178
+ // update summary
179
+ {
180
+ '@grafana/tsconfig': { prev: '', next: 'latest' },
181
+ }
182
+ )
183
+ ).toEqual({
184
+ '@grafana/tsconfig': 'latest', // updated
185
+ '@swc/core': '^1.2.162',
186
+ '@types/minimist': '^1.2.2',
187
+ '@types/mkdirp': '^1.0.2',
188
+ '@types/semver': '^8.0.0',
189
+ 'node-plop': '0.26.3',
190
+ });
191
+ });
192
+ });
193
+ });
@@ -0,0 +1,22 @@
1
+ import { TEMPLATE_PATHS } from '../../constants';
2
+ import { getPluginJson } from '../utils.plugin';
3
+
4
+ describe('Utils / Plugins', () => {
5
+ describe('getPluginJson()', () => {
6
+ test('should return the parsed plugin JSON if the file exits', () => {
7
+ const srcDir = `${TEMPLATE_PATHS.app}/src`;
8
+ const pluginJson = getPluginJson(srcDir);
9
+
10
+ expect(pluginJson).toBeDefined();
11
+ expect(pluginJson.type).toBe('app');
12
+ });
13
+
14
+ test('should throw an error if the plugin.json is not found', () => {
15
+ const srcDir = `${TEMPLATE_PATHS.app}/src/unknown`;
16
+
17
+ expect(() => {
18
+ getPluginJson(srcDir);
19
+ }).toThrow();
20
+ });
21
+ });
22
+ });
@@ -0,0 +1,44 @@
1
+ import { PLUGIN_TYPES } from '../../constants';
2
+ import { getTemplateFiles } from '../utils.templates';
3
+
4
+ describe('Utils / Templates', () => {
5
+ describe('getTemplateFiles()', () => {
6
+ test('should return with a list of files', () => {
7
+ expect(getTemplateFiles(PLUGIN_TYPES.app).length).toBeGreaterThan(0);
8
+ });
9
+
10
+ test('should be possible to filter templates by a sub-folder', () => {
11
+ const templateFiles = getTemplateFiles(PLUGIN_TYPES.app, '.config/types');
12
+
13
+ // Should have found at least one file
14
+ expect(templateFiles.length).toBeGreaterThan(0);
15
+
16
+ // A file that should live in this sub-directory
17
+ expect(templateFiles.find((t) => t.includes('custom.d.ts'))).not.toBeUndefined();
18
+
19
+ // Something that should not be found in this sub-directory
20
+ expect(templateFiles.find((t) => t.includes('Dockerfile'))).toBeUndefined();
21
+ });
22
+
23
+ test('should return an empty array when filtering by a non-existing sub-folder', () => {
24
+ const templateFiles = getTemplateFiles(PLUGIN_TYPES.app, '.config/types/foo/bar/unknown');
25
+
26
+ expect(Array.isArray(templateFiles)).toBe(true);
27
+ expect(templateFiles.length).toBe(0);
28
+ });
29
+
30
+ test('should be possible to filter for multiple different files', () => {
31
+ const templateFiles = getTemplateFiles(PLUGIN_TYPES.app, ['.eslintrc', '.prettierrc.js', 'tsconfig.json']);
32
+
33
+ expect(Array.isArray(templateFiles)).toBe(true);
34
+ expect(templateFiles.length).toBe(3);
35
+ });
36
+
37
+ test('should be possible to filter for a single file', () => {
38
+ const templateFiles = getTemplateFiles(PLUGIN_TYPES.app, '.config/types/custom.d.ts');
39
+
40
+ expect(Array.isArray(templateFiles)).toBe(true);
41
+ expect(templateFiles.length).toBe(1);
42
+ });
43
+ });
44
+ });
@@ -0,0 +1,41 @@
1
+ const { marked } = require('marked');
2
+ const TerminalRenderer = require('marked-terminal');
3
+ const { Confirm, Select } = require('enquirer');
4
+
5
+ marked.setOptions({
6
+ renderer: new TerminalRenderer(),
7
+ });
8
+
9
+ export function displayAsMarkdown(msg: string) {
10
+ return marked(msg);
11
+ }
12
+
13
+ export function displayArrayAsList(files: string[]) {
14
+ return ` - ${files.map((t) => `\`${t}\``).join('\n - ')}`;
15
+ }
16
+
17
+ export function printMessage(msg: string) {
18
+ console.log(displayAsMarkdown(`\n${msg}`));
19
+ }
20
+
21
+ export function printSuccessMessage(msg: string) {
22
+ console.log(displayAsMarkdown(`\n✔ ${msg}`));
23
+ }
24
+
25
+ export function confirmPrompt(message: string) {
26
+ const prompt = new Confirm({
27
+ name: 'question',
28
+ message: displayAsMarkdown(message),
29
+ });
30
+
31
+ return prompt.run();
32
+ }
33
+
34
+ export function selectPrompt(message: string, choices: string[]) {
35
+ const prompt = new Select({
36
+ choices,
37
+ message: displayAsMarkdown(message),
38
+ });
39
+
40
+ return prompt.run();
41
+ }