@grafana/create-plugin 5.0.0-canary.1018.f88ab25.0 → 5.0.0
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.
- package/CHANGELOG.md +24 -0
- package/dist/commands/generate/print-success-message.js +9 -1
- package/dist/commands/generate/prompt-user.js +24 -26
- package/dist/commands/generate/update-go-sdk-and-packages.js +19 -1
- package/dist/commands/generate.command.js +20 -9
- package/dist/constants.js +1 -3
- package/dist/utils/utils.prettifyFiles.js +1 -1
- package/dist/utils/utils.templates.js +1 -3
- package/fixtures/test-template/src/plugin.json +1 -1
- package/package.json +8 -8
- package/src/commands/generate/print-success-message.ts +13 -2
- package/src/commands/generate/prompt-user.ts +26 -28
- package/src/commands/generate/update-go-sdk-and-packages.ts +22 -1
- package/src/commands/generate.command.ts +23 -11
- package/src/constants.ts +3 -3
- package/src/types.ts +0 -2
- package/src/utils/utils.prettifyFiles.ts +1 -1
- package/src/utils/utils.templates.ts +1 -3
- package/templates/app/src/plugin.json +1 -1
- package/templates/common/.config/webpack/webpack.config.ts +17 -4
- package/templates/common/_package.json +2 -2
- package/templates/common/src/README.md +4 -6
- package/templates/datasource/src/plugin.json +1 -1
- package/templates/panel/src/plugin.json +1 -1
- package/templates/scenes-app/src/plugin.json +1 -1
- package/templates/common/.config/webpack/publicPath.ts +0 -17
- /package/templates/{.github → github}/workflows/ci.yml +0 -0
- /package/templates/{.github → github}/workflows/is-compatible.yml +0 -0
- /package/templates/{.github → github}/workflows/release.yml +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
# v5.0.0 (Wed Jul 31 2024)
|
|
2
|
+
|
|
3
|
+
#### 💥 Breaking Change
|
|
4
|
+
|
|
5
|
+
- Create-Plugin: Simplify Prompts [#1018](https://github.com/grafana/plugin-tools/pull/1018) ([@jackw](https://github.com/jackw))
|
|
6
|
+
|
|
7
|
+
#### Authors: 1
|
|
8
|
+
|
|
9
|
+
- Jack Westbrook ([@jackw](https://github.com/jackw))
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# v4.16.3 (Mon Jul 29 2024)
|
|
14
|
+
|
|
15
|
+
#### 🐛 Bug Fix
|
|
16
|
+
|
|
17
|
+
- Create Plugin: Template dynamic public path [#1023](https://github.com/grafana/plugin-tools/pull/1023) ([@jackw](https://github.com/jackw))
|
|
18
|
+
|
|
19
|
+
#### Authors: 1
|
|
20
|
+
|
|
21
|
+
- Jack Westbrook ([@jackw](https://github.com/jackw))
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
1
25
|
# v4.16.2 (Fri Jul 12 2024)
|
|
2
26
|
|
|
3
27
|
#### 🐛 Bug Fix
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { machine } from 'node:os';
|
|
1
2
|
import { displayAsMarkdown } from '../../utils/utils.console.js';
|
|
2
3
|
import { normalizeId } from '../../utils/utils.handlebars.js';
|
|
3
4
|
import { getPackageManagerFromUserAgent } from '../../utils/utils.packageManager.js';
|
|
@@ -11,7 +12,7 @@ export function printGenerateSuccessMessage(answers) {
|
|
|
11
12
|
`- \`${packageManagerName} run dev\` to build (and watch) the plugin frontend code.`,
|
|
12
13
|
...(answers.hasBackend
|
|
13
14
|
? [
|
|
14
|
-
|
|
15
|
+
`- ${getBackendCmd()} to build the plugin backend code. Rerun this command every time you edit your backend files.`,
|
|
15
16
|
]
|
|
16
17
|
: []),
|
|
17
18
|
'- `docker-compose up` to start a grafana development server.',
|
|
@@ -30,3 +31,10 @@ _Note: We strongly recommend creating a new Git repository by running \`git init
|
|
|
30
31
|
`;
|
|
31
32
|
console.log(displayAsMarkdown(msg));
|
|
32
33
|
}
|
|
34
|
+
function getBackendCmd() {
|
|
35
|
+
const platform = machine();
|
|
36
|
+
if (platform === 'arm64') {
|
|
37
|
+
return '`mage -v build:linuxARM64`';
|
|
38
|
+
}
|
|
39
|
+
return '`mage -v build:linux`';
|
|
40
|
+
}
|
|
@@ -3,17 +3,17 @@ import { PLUGIN_TYPES } from '../../constants.js';
|
|
|
3
3
|
export async function promptUser(argv) {
|
|
4
4
|
let answers = {};
|
|
5
5
|
const enquirer = new Enquirer();
|
|
6
|
-
for (const
|
|
7
|
-
const
|
|
8
|
-
if (argv.hasOwnProperty(name)) {
|
|
9
|
-
answers = { ...answers, [name]: argv[name] };
|
|
6
|
+
for (const p of prompts) {
|
|
7
|
+
const prompt = p(answers);
|
|
8
|
+
if (argv.hasOwnProperty(prompt.name)) {
|
|
9
|
+
answers = { ...answers, [prompt.name]: argv[prompt.name] };
|
|
10
10
|
}
|
|
11
11
|
else {
|
|
12
|
-
if (typeof shouldPrompt === 'function' && !shouldPrompt(answers)) {
|
|
12
|
+
if (typeof prompt.shouldPrompt === 'function' && !prompt.shouldPrompt(answers)) {
|
|
13
13
|
continue;
|
|
14
14
|
}
|
|
15
15
|
else {
|
|
16
|
-
const result = await enquirer.prompt(prompt
|
|
16
|
+
const result = await enquirer.prompt(prompt);
|
|
17
17
|
answers = { ...answers, ...result };
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -26,33 +26,37 @@ const prompts = [
|
|
|
26
26
|
type: 'select',
|
|
27
27
|
choices: [
|
|
28
28
|
{
|
|
29
|
-
|
|
29
|
+
message: 'App (add custom pages, UI extensions and bundle other plugins)',
|
|
30
30
|
value: PLUGIN_TYPES.app,
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
|
-
|
|
33
|
+
message: 'Data source (query data from a custom source)',
|
|
34
34
|
value: PLUGIN_TYPES.datasource,
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
|
|
37
|
+
message: 'Panel (add a visualization for data or a widget)',
|
|
38
38
|
value: PLUGIN_TYPES.panel,
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
|
|
41
|
+
message: 'App with Scenes (create dynamic dashboards in app pages)',
|
|
42
42
|
value: PLUGIN_TYPES.scenes,
|
|
43
43
|
},
|
|
44
44
|
],
|
|
45
|
-
message: 'Select plugin type',
|
|
46
|
-
}),
|
|
47
|
-
(answers) => ({
|
|
48
|
-
name: 'hasBackend',
|
|
49
|
-
type: 'confirm',
|
|
50
|
-
message: answers.pluginType === PLUGIN_TYPES.app
|
|
51
|
-
? 'Does your plugin require a backend to support server-side functionality (e.g. calling external APIs, custom backend logic, advanced authentication, etc)?'
|
|
52
|
-
: 'Does your plugin require a backend to support server-side functionality (e.g. alerting, advanced authentication, public dashboards, etc)?',
|
|
53
|
-
initial: false,
|
|
54
|
-
shouldPrompt: (answers) => answers.pluginType !== PLUGIN_TYPES.panel,
|
|
45
|
+
message: 'Select a plugin type',
|
|
55
46
|
}),
|
|
47
|
+
(answers) => {
|
|
48
|
+
const isAppType = answers.pluginType === PLUGIN_TYPES.app || answers.pluginType === PLUGIN_TYPES.scenes;
|
|
49
|
+
const message = isAppType
|
|
50
|
+
? 'Add a backend to support server-side functionality? (calling external APIs, custom backend logic, advanced authentication, etc)'
|
|
51
|
+
: 'Add a backend to support server-side functionality? (alerting, advanced authentication, public dashboards, etc)';
|
|
52
|
+
return {
|
|
53
|
+
name: 'hasBackend',
|
|
54
|
+
type: 'confirm',
|
|
55
|
+
message: message,
|
|
56
|
+
initial: false,
|
|
57
|
+
shouldPrompt: (answers) => answers.pluginType !== PLUGIN_TYPES.panel,
|
|
58
|
+
};
|
|
59
|
+
},
|
|
56
60
|
() => ({
|
|
57
61
|
name: 'pluginName',
|
|
58
62
|
type: 'input',
|
|
@@ -75,10 +79,4 @@ const prompts = [
|
|
|
75
79
|
return 'Organization name is required';
|
|
76
80
|
},
|
|
77
81
|
}),
|
|
78
|
-
() => ({
|
|
79
|
-
name: 'pluginDescription',
|
|
80
|
-
type: 'input',
|
|
81
|
-
message: 'How would you describe your plugin?',
|
|
82
|
-
initial: '',
|
|
83
|
-
}),
|
|
84
82
|
];
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import which from 'which';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
|
+
import createDebug from 'debug';
|
|
3
4
|
import { exec } from 'node:child_process';
|
|
4
5
|
const SDK_GO_MODULE = 'github.com/grafana/grafana-plugin-sdk-go';
|
|
6
|
+
const debug = createDebug('create-plugin:update-go');
|
|
5
7
|
export async function updateGoSdkAndModules(exportPath) {
|
|
6
8
|
const goModPath = `${exportPath}/go.mod`;
|
|
7
9
|
if (!fs.existsSync(goModPath)) {
|
|
@@ -12,19 +14,21 @@ export async function updateGoSdkAndModules(exportPath) {
|
|
|
12
14
|
return '';
|
|
13
15
|
}
|
|
14
16
|
try {
|
|
17
|
+
const version = await getLatestSdkVersion(exportPath);
|
|
15
18
|
await updateSdk(exportPath);
|
|
16
19
|
await updateGoMod(exportPath);
|
|
20
|
+
return `Updated Grafana go sdk to ${version} (latest)`;
|
|
17
21
|
}
|
|
18
22
|
catch {
|
|
19
23
|
throw new Error('There was an error trying to update the grafana go sdk. Please run `go get github.com/grafana/grafana-plugin-sdk-go` manually in your plugin directory.');
|
|
20
24
|
}
|
|
21
|
-
return 'Grafana go sdk updated successfully.';
|
|
22
25
|
}
|
|
23
26
|
function updateSdk(exportPath) {
|
|
24
27
|
return new Promise(async (resolve, reject) => {
|
|
25
28
|
const command = `go get ${SDK_GO_MODULE}`;
|
|
26
29
|
exec(command, { cwd: exportPath }, (error) => {
|
|
27
30
|
if (error) {
|
|
31
|
+
debug(error);
|
|
28
32
|
reject();
|
|
29
33
|
}
|
|
30
34
|
resolve();
|
|
@@ -36,9 +40,23 @@ function updateGoMod(exportPath) {
|
|
|
36
40
|
const command = `go mod tidy`;
|
|
37
41
|
exec(command, { cwd: exportPath }, (error) => {
|
|
38
42
|
if (error) {
|
|
43
|
+
debug(error);
|
|
39
44
|
reject();
|
|
40
45
|
}
|
|
41
46
|
resolve();
|
|
42
47
|
});
|
|
43
48
|
});
|
|
44
49
|
}
|
|
50
|
+
function getLatestSdkVersion(exportPath) {
|
|
51
|
+
return new Promise(async (resolve, reject) => {
|
|
52
|
+
const command = `go list -m -json ${SDK_GO_MODULE}@latest`;
|
|
53
|
+
exec(command, { cwd: exportPath }, (error, stdout) => {
|
|
54
|
+
if (error) {
|
|
55
|
+
debug(error);
|
|
56
|
+
reject();
|
|
57
|
+
}
|
|
58
|
+
const version = JSON.parse(stdout).Version;
|
|
59
|
+
resolve(version);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -2,7 +2,7 @@ import { glob } from 'glob';
|
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { EXTRA_TEMPLATE_VARIABLES, IS_DEV, TEMPLATE_PATHS } from '../constants.js';
|
|
5
|
+
import { EXTRA_TEMPLATE_VARIABLES, IS_DEV, PLUGIN_TYPES, TEMPLATE_PATHS } from '../constants.js';
|
|
6
6
|
import { printError } from '../utils/utils.console.js';
|
|
7
7
|
import { directoryExists, getExportFileName, isFile } from '../utils/utils.files.js';
|
|
8
8
|
import { getExportPath } from '../utils/utils.path.js';
|
|
@@ -22,9 +22,17 @@ export const generate = async (argv) => {
|
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
24
|
const actions = getTemplateActions({ templateData, exportPath });
|
|
25
|
-
const
|
|
25
|
+
const failures = await generateFiles({ actions });
|
|
26
|
+
const changes = [
|
|
27
|
+
`Scaffolded ${templateData.pluginId} ${templateData.pluginType} plugin ${templateData.hasBackend ? '(with Go backend)' : ''}`,
|
|
28
|
+
'Added basic E2E test (Playwright)',
|
|
29
|
+
`${provisioningMsg[templateData.pluginType]}`,
|
|
30
|
+
'Configured development environment (Docker)',
|
|
31
|
+
'Added default GitHub actions for CI, releases and Grafana compatibility',
|
|
32
|
+
];
|
|
33
|
+
console.log('');
|
|
26
34
|
changes.forEach((change) => {
|
|
27
|
-
console.log(`${chalk.green('✔︎
|
|
35
|
+
console.log(`${chalk.green('✔︎')} ${change}`);
|
|
28
36
|
});
|
|
29
37
|
failures.forEach((failure) => {
|
|
30
38
|
printError(`${failure.error}`);
|
|
@@ -33,6 +41,7 @@ export const generate = async (argv) => {
|
|
|
33
41
|
await execPostScaffoldFunction(updateGoSdkAndModules, exportPath);
|
|
34
42
|
}
|
|
35
43
|
await execPostScaffoldFunction(prettifyFiles, { targetPath: exportPath });
|
|
44
|
+
console.log('\n');
|
|
36
45
|
printGenerateSuccessMessage(templateData);
|
|
37
46
|
};
|
|
38
47
|
function getTemplateActions({ exportPath, templateData }) {
|
|
@@ -60,7 +69,7 @@ function getTemplateActions({ exportPath, templateData }) {
|
|
|
60
69
|
}, []);
|
|
61
70
|
const ciWorkflowActions = getActionsForTemplateFolder({
|
|
62
71
|
folderPath: TEMPLATE_PATHS.ciWorkflows,
|
|
63
|
-
exportPath,
|
|
72
|
+
exportPath: path.join(exportPath, '.github'),
|
|
64
73
|
templateData,
|
|
65
74
|
});
|
|
66
75
|
return [...pluginActions, ...ciWorkflowActions];
|
|
@@ -84,7 +93,6 @@ function getActionsForTemplateFolder({ folderPath, exportPath, templateData, })
|
|
|
84
93
|
}
|
|
85
94
|
async function generateFiles({ actions }) {
|
|
86
95
|
const failures = [];
|
|
87
|
-
const changes = [];
|
|
88
96
|
for (const action of actions) {
|
|
89
97
|
try {
|
|
90
98
|
const rootDir = path.dirname(action.path);
|
|
@@ -94,9 +102,6 @@ async function generateFiles({ actions }) {
|
|
|
94
102
|
}
|
|
95
103
|
const rendered = renderTemplateFromFile(action.templateFile, action.data);
|
|
96
104
|
await writeFile(action.path, rendered);
|
|
97
|
-
changes.push({
|
|
98
|
-
path: action.path,
|
|
99
|
-
});
|
|
100
105
|
}
|
|
101
106
|
catch (error) {
|
|
102
107
|
let message;
|
|
@@ -112,7 +117,7 @@ async function generateFiles({ actions }) {
|
|
|
112
117
|
});
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
|
-
return
|
|
120
|
+
return failures;
|
|
116
121
|
}
|
|
117
122
|
async function execPostScaffoldFunction(fn, ...args) {
|
|
118
123
|
try {
|
|
@@ -125,3 +130,9 @@ async function execPostScaffoldFunction(fn, ...args) {
|
|
|
125
130
|
printError(`${error}`);
|
|
126
131
|
}
|
|
127
132
|
}
|
|
133
|
+
const provisioningMsg = {
|
|
134
|
+
[PLUGIN_TYPES.app]: 'Set up provisioning for app',
|
|
135
|
+
[PLUGIN_TYPES.datasource]: 'Set up provisioning for data source instance',
|
|
136
|
+
[PLUGIN_TYPES.panel]: 'Set up provisioning for basic dashboard and TestData data source instance',
|
|
137
|
+
[PLUGIN_TYPES.scenes]: 'Set up provisioning for app and TestData data source instance',
|
|
138
|
+
};
|
package/dist/constants.js
CHANGED
|
@@ -16,15 +16,13 @@ export const TEMPLATE_PATHS = {
|
|
|
16
16
|
common: path.join(TEMPLATES_DIR, 'common'),
|
|
17
17
|
datasource: path.join(TEMPLATES_DIR, 'datasource'),
|
|
18
18
|
panel: path.join(TEMPLATES_DIR, 'panel'),
|
|
19
|
-
ciWorkflows: path.join(TEMPLATES_DIR, '
|
|
20
|
-
isCompatibleWorkflow: path.join(TEMPLATES_DIR, 'github', 'is-compatible'),
|
|
19
|
+
ciWorkflows: path.join(TEMPLATES_DIR, 'github'),
|
|
21
20
|
};
|
|
22
21
|
export var PLUGIN_TYPES;
|
|
23
22
|
(function (PLUGIN_TYPES) {
|
|
24
23
|
PLUGIN_TYPES["app"] = "app";
|
|
25
24
|
PLUGIN_TYPES["panel"] = "panel";
|
|
26
25
|
PLUGIN_TYPES["datasource"] = "datasource";
|
|
27
|
-
PLUGIN_TYPES["secretsmanager"] = "secretsmanager";
|
|
28
26
|
PLUGIN_TYPES["scenes"] = "scenesapp";
|
|
29
27
|
})(PLUGIN_TYPES || (PLUGIN_TYPES = {}));
|
|
30
28
|
export const EXTRA_TEMPLATE_VARIABLES = {
|
|
@@ -20,7 +20,7 @@ export async function prettifyFiles(options) {
|
|
|
20
20
|
catch (error) {
|
|
21
21
|
throw new Error('There was a problem running prettier on the plugin files. Please run `npx -y prettier@2 . --write` manually in your plugin directory.');
|
|
22
22
|
}
|
|
23
|
-
return '
|
|
23
|
+
return '';
|
|
24
24
|
}
|
|
25
25
|
function isPrettierUsed(projectRoot) {
|
|
26
26
|
return Boolean(getPrettierVersion(projectRoot));
|
|
@@ -12,7 +12,7 @@ import { getPackageManagerInstallCmd, getPackageManagerWithFallback, getPackageM
|
|
|
12
12
|
import { getExportFileName } from '../utils/utils.files.js';
|
|
13
13
|
import { getGrafanaRuntimeVersion, getVersion } from './utils.version.js';
|
|
14
14
|
import { getConfig } from './utils.config.js';
|
|
15
|
-
const debug = createDebug('templates');
|
|
15
|
+
const debug = createDebug('create-plugin:templates');
|
|
16
16
|
export function getTemplateFiles(pluginType, filter) {
|
|
17
17
|
const commonFiles = glob.sync(`${TEMPLATE_PATHS.common}/**`, { dot: true });
|
|
18
18
|
const pluginTypeSpecificFiles = glob.sync(`${TEMPLATE_PATHS[pluginType]}/**`, { dot: true });
|
|
@@ -73,7 +73,6 @@ export function getTemplateData(cliArgs) {
|
|
|
73
73
|
...EXTRA_TEMPLATE_VARIABLES,
|
|
74
74
|
pluginId: normalizeId(cliArgs.pluginName, cliArgs.orgName, cliArgs.pluginType),
|
|
75
75
|
pluginName: cliArgs.pluginName,
|
|
76
|
-
pluginDescription: cliArgs.pluginDescription,
|
|
77
76
|
hasBackend: cliArgs.hasBackend,
|
|
78
77
|
orgName: cliArgs.orgName,
|
|
79
78
|
pluginType: cliArgs.pluginType,
|
|
@@ -97,7 +96,6 @@ export function getTemplateData(cliArgs) {
|
|
|
97
96
|
...EXTRA_TEMPLATE_VARIABLES,
|
|
98
97
|
pluginId: pluginJson.id,
|
|
99
98
|
pluginName: pluginJson.name,
|
|
100
|
-
pluginDescription: pluginJson.info.description,
|
|
101
99
|
hasBackend: pluginJson.backend,
|
|
102
100
|
orgName: pluginJson.info.author.name,
|
|
103
101
|
pluginType: pluginJson.type,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grafana/create-plugin",
|
|
3
|
-
"version": "5.0.0
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"directory": "packages/create-plugin",
|
|
6
6
|
"url": "https://github.com/grafana/plugin-tools"
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
"dev-scenes-app": "nodemon --exec 'npm run generate-scenes-app'",
|
|
29
29
|
"dev-panel": "nodemon --exec 'npm run generate-panel'",
|
|
30
30
|
"dev-datasource": "nodemon --exec 'npm run generate-datasource'",
|
|
31
|
-
"generate-app": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample app' --orgName='sample-org' --
|
|
32
|
-
"generate-scenes-app": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample scenesapp' --orgName='sample-org' --
|
|
33
|
-
"generate-app-backend": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample app' --orgName='sample-org' --
|
|
34
|
-
"generate-panel": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample panel' --orgName='sample-org' --
|
|
35
|
-
"generate-datasource": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample datasource' --orgName='sample-org' --
|
|
36
|
-
"generate-datasource-backend": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample datasource' --orgName='sample-org' --
|
|
31
|
+
"generate-app": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample app' --orgName='sample-org' --pluginType='app' --no-hasBackend",
|
|
32
|
+
"generate-scenes-app": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample scenesapp' --orgName='sample-org' --pluginType='scenesapp' --no-hasBackend",
|
|
33
|
+
"generate-app-backend": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample app' --orgName='sample-org' --pluginType='app' --hasBackend",
|
|
34
|
+
"generate-panel": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample panel' --orgName='sample-org' --pluginType='panel'",
|
|
35
|
+
"generate-datasource": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample datasource' --orgName='sample-org' --pluginType='datasource' --no-hasBackend",
|
|
36
|
+
"generate-datasource-backend": "tsc && npm run clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js --pluginName='Sample datasource' --orgName='sample-org' --pluginType='datasource' --hasBackend",
|
|
37
37
|
"lint": "eslint --cache --ext .js,.jsx,.ts,.tsx ./src",
|
|
38
38
|
"lint:fix": "npm run lint -- --fix",
|
|
39
39
|
"test": "vitest",
|
|
@@ -87,5 +87,5 @@
|
|
|
87
87
|
"engines": {
|
|
88
88
|
"node": ">=20"
|
|
89
89
|
},
|
|
90
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "56c4ac2b94ab9d83be84be4379a90751359fe66e"
|
|
91
91
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { machine } from 'node:os';
|
|
2
|
+
import { TemplateData } from '../../types.js';
|
|
1
3
|
import { displayAsMarkdown } from '../../utils/utils.console.js';
|
|
2
4
|
import { normalizeId } from '../../utils/utils.handlebars.js';
|
|
3
5
|
import { getPackageManagerFromUserAgent } from '../../utils/utils.packageManager.js';
|
|
4
|
-
import { TemplateData } from '../../types.js';
|
|
5
6
|
|
|
6
7
|
export function printGenerateSuccessMessage(answers: TemplateData) {
|
|
7
8
|
const directory = normalizeId(answers.pluginName, answers.orgName, answers.pluginType);
|
|
8
9
|
const { packageManagerName } = getPackageManagerFromUserAgent();
|
|
10
|
+
|
|
9
11
|
const commands = [
|
|
10
12
|
`- \`cd ./${directory}\``,
|
|
11
13
|
`- \`${packageManagerName} install\` to install frontend dependencies.`,
|
|
@@ -13,7 +15,7 @@ export function printGenerateSuccessMessage(answers: TemplateData) {
|
|
|
13
15
|
`- \`${packageManagerName} run dev\` to build (and watch) the plugin frontend code.`,
|
|
14
16
|
...(answers.hasBackend
|
|
15
17
|
? [
|
|
16
|
-
|
|
18
|
+
`- ${getBackendCmd()} to build the plugin backend code. Rerun this command every time you edit your backend files.`,
|
|
17
19
|
]
|
|
18
20
|
: []),
|
|
19
21
|
'- `docker-compose up` to start a grafana development server.',
|
|
@@ -34,3 +36,12 @@ _Note: We strongly recommend creating a new Git repository by running \`git init
|
|
|
34
36
|
|
|
35
37
|
console.log(displayAsMarkdown(msg));
|
|
36
38
|
}
|
|
39
|
+
|
|
40
|
+
function getBackendCmd() {
|
|
41
|
+
const platform = machine();
|
|
42
|
+
if (platform === 'arm64') {
|
|
43
|
+
return '`mage -v build:linuxARM64`';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return '`mage -v build:linux`';
|
|
47
|
+
}
|
|
@@ -7,16 +7,16 @@ export async function promptUser(argv: minimist.ParsedArgs) {
|
|
|
7
7
|
let answers: Partial<GenerateCliArgs> = {};
|
|
8
8
|
const enquirer = new Enquirer();
|
|
9
9
|
|
|
10
|
-
for (const
|
|
11
|
-
const
|
|
10
|
+
for (const p of prompts) {
|
|
11
|
+
const prompt = p(answers);
|
|
12
12
|
|
|
13
|
-
if (argv.hasOwnProperty(name)) {
|
|
14
|
-
answers = { ...answers, [name]: argv[name] };
|
|
13
|
+
if (argv.hasOwnProperty(prompt.name)) {
|
|
14
|
+
answers = { ...answers, [prompt.name]: argv[prompt.name] };
|
|
15
15
|
} else {
|
|
16
|
-
if (typeof shouldPrompt === 'function' && !shouldPrompt(answers)) {
|
|
16
|
+
if (typeof prompt.shouldPrompt === 'function' && !prompt.shouldPrompt(answers)) {
|
|
17
17
|
continue;
|
|
18
18
|
} else {
|
|
19
|
-
const result = await enquirer.prompt(prompt
|
|
19
|
+
const result = await enquirer.prompt(prompt);
|
|
20
20
|
answers = { ...answers, ...result };
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -36,7 +36,7 @@ type Prompt = {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
type Choice = {
|
|
39
|
-
name
|
|
39
|
+
name?: string;
|
|
40
40
|
message?: string;
|
|
41
41
|
value?: unknown;
|
|
42
42
|
hint?: string;
|
|
@@ -51,34 +51,38 @@ const prompts: Array<(answers: Partial<GenerateCliArgs>) => Prompt> = [
|
|
|
51
51
|
type: 'select',
|
|
52
52
|
choices: [
|
|
53
53
|
{
|
|
54
|
-
|
|
54
|
+
message: 'App (add custom pages, UI extensions and bundle other plugins)',
|
|
55
55
|
value: PLUGIN_TYPES.app,
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
|
-
|
|
58
|
+
message: 'Data source (query data from a custom source)',
|
|
59
59
|
value: PLUGIN_TYPES.datasource,
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
|
-
|
|
62
|
+
message: 'Panel (add a visualization for data or a widget)',
|
|
63
63
|
value: PLUGIN_TYPES.panel,
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
|
-
|
|
66
|
+
message: 'App with Scenes (create dynamic dashboards in app pages)',
|
|
67
67
|
value: PLUGIN_TYPES.scenes,
|
|
68
68
|
},
|
|
69
69
|
],
|
|
70
|
-
message: 'Select plugin type',
|
|
71
|
-
}),
|
|
72
|
-
(answers) => ({
|
|
73
|
-
name: 'hasBackend',
|
|
74
|
-
type: 'confirm',
|
|
75
|
-
message:
|
|
76
|
-
answers.pluginType === PLUGIN_TYPES.app
|
|
77
|
-
? 'Does your plugin require a backend to support server-side functionality (e.g. calling external APIs, custom backend logic, advanced authentication, etc)?'
|
|
78
|
-
: 'Does your plugin require a backend to support server-side functionality (e.g. alerting, advanced authentication, public dashboards, etc)?',
|
|
79
|
-
initial: false,
|
|
80
|
-
shouldPrompt: (answers) => answers.pluginType !== PLUGIN_TYPES.panel,
|
|
70
|
+
message: 'Select a plugin type',
|
|
81
71
|
}),
|
|
72
|
+
(answers) => {
|
|
73
|
+
const isAppType = answers.pluginType === PLUGIN_TYPES.app || answers.pluginType === PLUGIN_TYPES.scenes;
|
|
74
|
+
const message = isAppType
|
|
75
|
+
? 'Add a backend to support server-side functionality? (calling external APIs, custom backend logic, advanced authentication, etc)'
|
|
76
|
+
: 'Add a backend to support server-side functionality? (alerting, advanced authentication, public dashboards, etc)';
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
name: 'hasBackend',
|
|
80
|
+
type: 'confirm',
|
|
81
|
+
message: message,
|
|
82
|
+
initial: false,
|
|
83
|
+
shouldPrompt: (answers) => answers.pluginType !== PLUGIN_TYPES.panel,
|
|
84
|
+
};
|
|
85
|
+
},
|
|
82
86
|
() => ({
|
|
83
87
|
name: 'pluginName',
|
|
84
88
|
type: 'input',
|
|
@@ -101,10 +105,4 @@ const prompts: Array<(answers: Partial<GenerateCliArgs>) => Prompt> = [
|
|
|
101
105
|
return 'Organization name is required';
|
|
102
106
|
},
|
|
103
107
|
}),
|
|
104
|
-
() => ({
|
|
105
|
-
name: 'pluginDescription',
|
|
106
|
-
type: 'input',
|
|
107
|
-
message: 'How would you describe your plugin?',
|
|
108
|
-
initial: '',
|
|
109
|
-
}),
|
|
110
108
|
];
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import which from 'which';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
|
+
import createDebug from 'debug';
|
|
3
4
|
import { exec } from 'node:child_process';
|
|
4
5
|
|
|
5
6
|
const SDK_GO_MODULE = 'github.com/grafana/grafana-plugin-sdk-go';
|
|
6
7
|
|
|
8
|
+
const debug = createDebug('create-plugin:update-go');
|
|
9
|
+
|
|
7
10
|
export async function updateGoSdkAndModules(exportPath: string) {
|
|
8
11
|
// check if there is a go.mod file in exportPath
|
|
9
12
|
const goModPath = `${exportPath}/go.mod`;
|
|
@@ -20,14 +23,15 @@ export async function updateGoSdkAndModules(exportPath: string) {
|
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
try {
|
|
26
|
+
const version = await getLatestSdkVersion(exportPath);
|
|
23
27
|
await updateSdk(exportPath);
|
|
24
28
|
await updateGoMod(exportPath);
|
|
29
|
+
return `Updated Grafana go sdk to ${version} (latest)`;
|
|
25
30
|
} catch {
|
|
26
31
|
throw new Error(
|
|
27
32
|
'There was an error trying to update the grafana go sdk. Please run `go get github.com/grafana/grafana-plugin-sdk-go` manually in your plugin directory.'
|
|
28
33
|
);
|
|
29
34
|
}
|
|
30
|
-
return 'Grafana go sdk updated successfully.';
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
function updateSdk(exportPath: string): Promise<void> {
|
|
@@ -36,6 +40,7 @@ function updateSdk(exportPath: string): Promise<void> {
|
|
|
36
40
|
const command = `go get ${SDK_GO_MODULE}`;
|
|
37
41
|
exec(command, { cwd: exportPath }, (error) => {
|
|
38
42
|
if (error) {
|
|
43
|
+
debug(error);
|
|
39
44
|
reject();
|
|
40
45
|
}
|
|
41
46
|
resolve();
|
|
@@ -49,9 +54,25 @@ function updateGoMod(exportPath: string): Promise<void> {
|
|
|
49
54
|
const command = `go mod tidy`;
|
|
50
55
|
exec(command, { cwd: exportPath }, (error) => {
|
|
51
56
|
if (error) {
|
|
57
|
+
debug(error);
|
|
52
58
|
reject();
|
|
53
59
|
}
|
|
54
60
|
resolve();
|
|
55
61
|
});
|
|
56
62
|
});
|
|
57
63
|
}
|
|
64
|
+
|
|
65
|
+
function getLatestSdkVersion(exportPath: string): Promise<string> {
|
|
66
|
+
return new Promise(async (resolve, reject) => {
|
|
67
|
+
// run go list SDK_GO_MODULE@latest to get the latest version number
|
|
68
|
+
const command = `go list -m -json ${SDK_GO_MODULE}@latest`;
|
|
69
|
+
exec(command, { cwd: exportPath }, (error, stdout) => {
|
|
70
|
+
if (error) {
|
|
71
|
+
debug(error);
|
|
72
|
+
reject();
|
|
73
|
+
}
|
|
74
|
+
const version = JSON.parse(stdout).Version;
|
|
75
|
+
resolve(version);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
@@ -3,7 +3,7 @@ import minimist from 'minimist';
|
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import { mkdir, readdir, writeFile } from 'node:fs/promises';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
-
import { EXTRA_TEMPLATE_VARIABLES, IS_DEV, TEMPLATE_PATHS } from '../constants.js';
|
|
6
|
+
import { EXTRA_TEMPLATE_VARIABLES, IS_DEV, PLUGIN_TYPES, TEMPLATE_PATHS } from '../constants.js';
|
|
7
7
|
import { printError } from '../utils/utils.console.js';
|
|
8
8
|
import { directoryExists, getExportFileName, isFile } from '../utils/utils.files.js';
|
|
9
9
|
import { getExportPath } from '../utils/utils.path.js';
|
|
@@ -28,10 +28,19 @@ export const generate = async (argv: minimist.ParsedArgs) => {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const actions = getTemplateActions({ templateData, exportPath });
|
|
31
|
-
const
|
|
32
|
-
|
|
31
|
+
const failures = await generateFiles({ actions });
|
|
32
|
+
const changes = [
|
|
33
|
+
`Scaffolded ${templateData.pluginId} ${templateData.pluginType} plugin ${
|
|
34
|
+
templateData.hasBackend ? '(with Go backend)' : ''
|
|
35
|
+
}`,
|
|
36
|
+
'Added basic E2E test (Playwright)',
|
|
37
|
+
`${provisioningMsg[templateData.pluginType]}`,
|
|
38
|
+
'Configured development environment (Docker)',
|
|
39
|
+
'Added default GitHub actions for CI, releases and Grafana compatibility',
|
|
40
|
+
];
|
|
41
|
+
console.log('');
|
|
33
42
|
changes.forEach((change) => {
|
|
34
|
-
console.log(`${chalk.green('✔︎
|
|
43
|
+
console.log(`${chalk.green('✔︎')} ${change}`);
|
|
35
44
|
});
|
|
36
45
|
|
|
37
46
|
failures.forEach((failure) => {
|
|
@@ -42,7 +51,7 @@ export const generate = async (argv: minimist.ParsedArgs) => {
|
|
|
42
51
|
await execPostScaffoldFunction(updateGoSdkAndModules, exportPath);
|
|
43
52
|
}
|
|
44
53
|
await execPostScaffoldFunction(prettifyFiles, { targetPath: exportPath });
|
|
45
|
-
|
|
54
|
+
console.log('\n');
|
|
46
55
|
printGenerateSuccessMessage(templateData);
|
|
47
56
|
};
|
|
48
57
|
|
|
@@ -91,7 +100,7 @@ function getTemplateActions({ exportPath, templateData }: { exportPath: string;
|
|
|
91
100
|
// Copy over Github workflow files
|
|
92
101
|
const ciWorkflowActions = getActionsForTemplateFolder({
|
|
93
102
|
folderPath: TEMPLATE_PATHS.ciWorkflows,
|
|
94
|
-
exportPath,
|
|
103
|
+
exportPath: path.join(exportPath, '.github'),
|
|
95
104
|
templateData,
|
|
96
105
|
});
|
|
97
106
|
|
|
@@ -131,7 +140,6 @@ function getActionsForTemplateFolder({
|
|
|
131
140
|
|
|
132
141
|
async function generateFiles({ actions }: { actions: any[] }) {
|
|
133
142
|
const failures = [];
|
|
134
|
-
const changes = [];
|
|
135
143
|
for (const action of actions) {
|
|
136
144
|
try {
|
|
137
145
|
const rootDir = path.dirname(action.path);
|
|
@@ -142,9 +150,6 @@ async function generateFiles({ actions }: { actions: any[] }) {
|
|
|
142
150
|
|
|
143
151
|
const rendered = renderTemplateFromFile(action.templateFile, action.data);
|
|
144
152
|
await writeFile(action.path, rendered);
|
|
145
|
-
changes.push({
|
|
146
|
-
path: action.path,
|
|
147
|
-
});
|
|
148
153
|
} catch (error) {
|
|
149
154
|
let message;
|
|
150
155
|
if (error instanceof Error) {
|
|
@@ -158,7 +163,7 @@ async function generateFiles({ actions }: { actions: any[] }) {
|
|
|
158
163
|
});
|
|
159
164
|
}
|
|
160
165
|
}
|
|
161
|
-
return
|
|
166
|
+
return failures;
|
|
162
167
|
}
|
|
163
168
|
|
|
164
169
|
type AsyncFunction<T> = (...args: any[]) => Promise<T>;
|
|
@@ -173,3 +178,10 @@ async function execPostScaffoldFunction<T>(fn: AsyncFunction<T>, ...args: Parame
|
|
|
173
178
|
printError(`${error}`);
|
|
174
179
|
}
|
|
175
180
|
}
|
|
181
|
+
|
|
182
|
+
const provisioningMsg = {
|
|
183
|
+
[PLUGIN_TYPES.app]: 'Set up provisioning for app',
|
|
184
|
+
[PLUGIN_TYPES.datasource]: 'Set up provisioning for data source instance',
|
|
185
|
+
[PLUGIN_TYPES.panel]: 'Set up provisioning for basic dashboard and TestData data source instance',
|
|
186
|
+
[PLUGIN_TYPES.scenes]: 'Set up provisioning for app and TestData data source instance',
|
|
187
|
+
};
|
package/src/constants.ts
CHANGED
|
@@ -27,15 +27,15 @@ export const TEMPLATE_PATHS: Record<string, string> = {
|
|
|
27
27
|
common: path.join(TEMPLATES_DIR, 'common'),
|
|
28
28
|
datasource: path.join(TEMPLATES_DIR, 'datasource'),
|
|
29
29
|
panel: path.join(TEMPLATES_DIR, 'panel'),
|
|
30
|
-
ciWorkflows: path.join(TEMPLATES_DIR, '
|
|
31
|
-
isCompatibleWorkflow: path.join(TEMPLATES_DIR, 'github', 'is-compatible'),
|
|
30
|
+
ciWorkflows: path.join(TEMPLATES_DIR, 'github'),
|
|
32
31
|
};
|
|
33
32
|
|
|
34
33
|
export enum PLUGIN_TYPES {
|
|
35
34
|
app = 'app',
|
|
36
35
|
panel = 'panel',
|
|
37
36
|
datasource = 'datasource',
|
|
38
|
-
|
|
37
|
+
// TODO: Don't understand why this is here. Cannot create a secretsmanager or a renderer.
|
|
38
|
+
// secretsmanager = 'secretsmanager',
|
|
39
39
|
scenes = 'scenesapp',
|
|
40
40
|
}
|
|
41
41
|
|
package/src/types.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { PLUGIN_TYPES } from './constants.js';
|
|
|
4
4
|
// (Either via user prompts or CLI arguments)
|
|
5
5
|
export type GenerateCliArgs = {
|
|
6
6
|
pluginName: string;
|
|
7
|
-
pluginDescription: string;
|
|
8
7
|
orgName: string;
|
|
9
8
|
pluginType: PLUGIN_TYPES;
|
|
10
9
|
hasBackend: boolean;
|
|
@@ -13,7 +12,6 @@ export type GenerateCliArgs = {
|
|
|
13
12
|
export type TemplateData = {
|
|
14
13
|
pluginId: string;
|
|
15
14
|
pluginName: string;
|
|
16
|
-
pluginDescription: string;
|
|
17
15
|
hasBackend: boolean;
|
|
18
16
|
orgName: string;
|
|
19
17
|
pluginType: PLUGIN_TYPES;
|
|
@@ -35,7 +35,7 @@ export async function prettifyFiles(options: PrettifyFilesArgs) {
|
|
|
35
35
|
'There was a problem running prettier on the plugin files. Please run `npx -y prettier@2 . --write` manually in your plugin directory.'
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
|
-
return '
|
|
38
|
+
return '';
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
function isPrettierUsed(projectRoot?: string) {
|
|
@@ -24,7 +24,7 @@ import { getExportFileName } from '../utils/utils.files.js';
|
|
|
24
24
|
import { getGrafanaRuntimeVersion, getVersion } from './utils.version.js';
|
|
25
25
|
import { getConfig } from './utils.config.js';
|
|
26
26
|
|
|
27
|
-
const debug = createDebug('templates');
|
|
27
|
+
const debug = createDebug('create-plugin:templates');
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
*
|
|
@@ -118,7 +118,6 @@ export function getTemplateData(cliArgs?: GenerateCliArgs): TemplateData {
|
|
|
118
118
|
...EXTRA_TEMPLATE_VARIABLES,
|
|
119
119
|
pluginId: normalizeId(cliArgs.pluginName, cliArgs.orgName, cliArgs.pluginType),
|
|
120
120
|
pluginName: cliArgs.pluginName,
|
|
121
|
-
pluginDescription: cliArgs.pluginDescription,
|
|
122
121
|
hasBackend: cliArgs.hasBackend,
|
|
123
122
|
orgName: cliArgs.orgName,
|
|
124
123
|
pluginType: cliArgs.pluginType,
|
|
@@ -144,7 +143,6 @@ export function getTemplateData(cliArgs?: GenerateCliArgs): TemplateData {
|
|
|
144
143
|
...EXTRA_TEMPLATE_VARIABLES,
|
|
145
144
|
pluginId: pluginJson.id,
|
|
146
145
|
pluginName: pluginJson.name,
|
|
147
|
-
pluginDescription: pluginJson.info.description,
|
|
148
146
|
hasBackend: pluginJson.backend,
|
|
149
147
|
orgName: pluginJson.info.author.name,
|
|
150
148
|
pluginType: pluginJson.type,
|
|
@@ -8,18 +8,30 @@
|
|
|
8
8
|
import CopyWebpackPlugin from 'copy-webpack-plugin';
|
|
9
9
|
import ESLintPlugin from 'eslint-webpack-plugin';
|
|
10
10
|
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
|
|
11
|
-
import LiveReloadPlugin from 'webpack-livereload-plugin';
|
|
12
11
|
import path from 'path';
|
|
13
12
|
import ReplaceInFileWebpackPlugin from 'replace-in-file-webpack-plugin';
|
|
14
13
|
import TerserPlugin from 'terser-webpack-plugin';
|
|
15
14
|
import { type Configuration, BannerPlugin } from 'webpack';
|
|
15
|
+
import LiveReloadPlugin from 'webpack-livereload-plugin';
|
|
16
|
+
import VirtualModulesPlugin from 'webpack-virtual-modules';
|
|
16
17
|
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
18
|
+
import { DIST_DIR, SOURCE_DIR } from './constants';
|
|
19
|
+
import { getCPConfigVersion, getEntries, getPackageJson, getPluginJson, hasReadme, isWSL } from './utils';
|
|
19
20
|
|
|
20
21
|
const pluginJson = getPluginJson();
|
|
21
22
|
const cpVersion = getCPConfigVersion();
|
|
22
23
|
|
|
24
|
+
const virtualPublicPath = new VirtualModulesPlugin({
|
|
25
|
+
'node_modules/grafana-public-path.js': `
|
|
26
|
+
import amdMetaModule from 'amd-module';
|
|
27
|
+
|
|
28
|
+
__webpack_public_path__ =
|
|
29
|
+
amdMetaModule && amdMetaModule.uri
|
|
30
|
+
? amdMetaModule.uri.slice(0, amdMetaModule.uri.lastIndexOf('/') + 1)
|
|
31
|
+
: 'public/plugins/${pluginJson.id}/';
|
|
32
|
+
`,
|
|
33
|
+
});
|
|
34
|
+
|
|
23
35
|
const config = async (env): Promise<Configuration> => {
|
|
24
36
|
const baseConfig: Configuration = {
|
|
25
37
|
cache: {
|
|
@@ -112,7 +124,7 @@ const config = async (env): Promise<Configuration> => {
|
|
|
112
124
|
{
|
|
113
125
|
loader: 'imports-loader',
|
|
114
126
|
options: {
|
|
115
|
-
imports: `side-effects
|
|
127
|
+
imports: `side-effects grafana-public-path`,
|
|
116
128
|
},
|
|
117
129
|
},
|
|
118
130
|
],
|
|
@@ -169,6 +181,7 @@ const config = async (env): Promise<Configuration> => {
|
|
|
169
181
|
},
|
|
170
182
|
|
|
171
183
|
plugins: [
|
|
184
|
+
virtualPublicPath,
|
|
172
185
|
// Insert create plugin version information into the bundle
|
|
173
186
|
new BannerPlugin({
|
|
174
187
|
banner: "/* [create-plugin] version: " + cpVersion + " */",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "{{ kebabCase pluginName }}",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"description": "{{ sentenceCase pluginDescription }}",
|
|
5
4
|
"scripts": {
|
|
6
5
|
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",
|
|
7
6
|
"dev": "webpack -w -c ./.config/webpack/webpack.config.ts --env development",
|
|
@@ -57,7 +56,8 @@
|
|
|
57
56
|
"typescript": "4.8.4",
|
|
58
57
|
"webpack": "^5.86.0",
|
|
59
58
|
"webpack-cli": "^5.1.4",
|
|
60
|
-
"webpack-livereload-plugin": "^3.0.2"
|
|
59
|
+
"webpack-livereload-plugin": "^3.0.2",
|
|
60
|
+
"webpack-virtual-modules": "^0.6.2"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">=20"
|
|
@@ -3,19 +3,17 @@
|
|
|
3
3
|
Remove any remaining comments before publishing as these may be displayed on Grafana.com -->
|
|
4
4
|
# {{ titleCase pluginName }}
|
|
5
5
|
|
|
6
|
-
{{ sentenceCase pluginDescription }}
|
|
7
|
-
|
|
8
6
|
<!-- To help maximize the impact of your README and improve usability for users, we propose the following loose structure:
|
|
9
7
|
|
|
10
8
|
**BEFORE YOU BEGIN**
|
|
11
9
|
- Ensure all links are absolute URLs so that they will work when the README is displayed within Grafana and Grafana.com
|
|
12
|
-
- Be inspired ✨
|
|
10
|
+
- Be inspired ✨
|
|
13
11
|
- [grafana-polystat-panel](https://github.com/grafana/grafana-polystat-panel)
|
|
14
12
|
- [volkovlabs-variable-panel](https://github.com/volkovlabs/volkovlabs-variable-panel)
|
|
15
13
|
|
|
16
14
|
**ADD SOME BADGES**
|
|
17
15
|
|
|
18
|
-
Badges convey useful information at a glance for users whether in the Catalog or viewing the source code. You can use the generator on [Shields.io](https://shields.io/badges/dynamic-json-badge) together with the Grafana.com API
|
|
16
|
+
Badges convey useful information at a glance for users whether in the Catalog or viewing the source code. You can use the generator on [Shields.io](https://shields.io/badges/dynamic-json-badge) together with the Grafana.com API
|
|
19
17
|
to create dynamic badges that update automatically when you publish a new version to the marketplace.
|
|
20
18
|
|
|
21
19
|
- For the logo field use 'grafana'.
|
|
@@ -30,7 +28,7 @@ Full example:  as you feel appropriate for your project.
|
|
31
29
|
|
|
32
30
|
## Overview / Introduction
|
|
33
|
-
Provide one or more paragraphs as an introduction to your plugin to help users understand why they should use it.
|
|
31
|
+
Provide one or more paragraphs as an introduction to your plugin to help users understand why they should use it.
|
|
34
32
|
|
|
35
33
|
Consider including screenshots:
|
|
36
34
|
- in [plugin.json](https://grafana.com/developers/plugin-tools/reference/plugin-json#info) include them as relative links.
|
|
@@ -47,4 +45,4 @@ If your project has dedicated documentation available for users, provide links h
|
|
|
47
45
|
|
|
48
46
|
## Contributing
|
|
49
47
|
Do you want folks to contribute to the plugin or provide feedback through specific means? If so, tell them how!
|
|
50
|
-
-->
|
|
48
|
+
-->
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* ⚠️⚠️⚠️ THIS FILE WAS SCAFFOLDED BY `@grafana/create-plugin`. DO NOT EDIT THIS FILE DIRECTLY. ⚠️⚠️⚠️
|
|
3
|
-
*
|
|
4
|
-
* This file dynamically sets the public path at runtime based on the location of the plugin's AMD module.
|
|
5
|
-
* It relies on the magic `module` which is defined by the AMD loader.
|
|
6
|
-
* https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#module
|
|
7
|
-
*
|
|
8
|
-
* We fallback to the plugin root so that older versions of Grafana will continue to load the plugin correctly.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// @ts-nocheck
|
|
12
|
-
import amdMetaModule from 'amd-module';
|
|
13
|
-
|
|
14
|
-
__webpack_public_path__ =
|
|
15
|
-
amdMetaModule && amdMetaModule.uri
|
|
16
|
-
? amdMetaModule.uri.slice(0, amdMetaModule.uri.lastIndexOf('/') + 1)
|
|
17
|
-
: 'public/plugins/{{ pluginId }}/';
|
|
File without changes
|
|
File without changes
|
|
File without changes
|