@grafana/create-plugin 0.2.0 → 0.3.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.
Files changed (42) hide show
  1. package/CONTRIBUTING.md +45 -8
  2. package/README.md +25 -62
  3. package/dist/commands/generate.plopfile.js +2 -1
  4. package/dist/commands/update.command.js +1 -1
  5. package/dist/constants.js +6 -3
  6. package/dist/utils/tests/utils.handlebars.test.js +3 -3
  7. package/dist/utils/tests/utils.plugin.test.js +1 -1
  8. package/dist/utils/utils.handlebars.js +4 -2
  9. package/fixtures/test-template/src/plugin.json +35 -0
  10. package/jest.config.js +2 -17
  11. package/package.json +12 -24
  12. package/scripts/generate-app.js +24 -0
  13. package/scripts/generate-datasource.js +24 -0
  14. package/scripts/generate-panel.js +23 -0
  15. package/src/commands/generate.plopfile.ts +1 -0
  16. package/src/commands/update.command.ts +1 -1
  17. package/src/constants.ts +7 -2
  18. package/src/utils/tests/utils.handlebars.test.ts +3 -3
  19. package/src/utils/tests/utils.plugin.test.ts +2 -2
  20. package/src/utils/utils.handlebars.ts +5 -2
  21. package/templates/app/src/plugin.json +4 -1
  22. package/templates/backend/go.mod +55 -2
  23. package/templates/backend/go.sum +91 -4
  24. package/templates/common/.config/README.md +4 -4
  25. package/templates/common/.config/jest.config.js +5 -1
  26. package/templates/common/.config/tsconfig.json +8 -0
  27. package/templates/common/.config/webpack/webpack.config.ts +29 -16
  28. package/templates/common/.nvmrc +1 -1
  29. package/templates/common/jest.config.js +4 -0
  30. package/templates/common/package.json +8 -3
  31. package/templates/datasource/src/plugin.json +2 -2
  32. package/templates/github/ci/.github/workflows/ci.yml +11 -2
  33. package/templates/github/ci/.github/workflows/release.yml +3 -3
  34. package/templates/panel/src/plugin.json +1 -0
  35. package/tsconfig.json +4 -13
  36. package/.eslintrc +0 -6
  37. package/.github/workflows/create-release.yml +0 -49
  38. package/.github/workflows/npm-bump-version.yml +0 -51
  39. package/.github/workflows/test.yml +0 -31
  40. package/.prettierrc +0 -10
  41. package/docs/example.mp4 +0 -0
  42. package/templates/common/.config/webpack/tsconfig.webpack.json +0 -9
package/CONTRIBUTING.md CHANGED
@@ -12,8 +12,8 @@ You need to have `npm` or `yarn` installed.
12
12
  ### Installing
13
13
 
14
14
  ```bash
15
- git clone git@github.com:grafana/create-plugin.git
16
- cd create-plugin
15
+ git clone git@github.com:grafana/plugin-tools.git
16
+ cd plugin-tools
17
17
  yarn install
18
18
  ```
19
19
 
@@ -25,18 +25,55 @@ yarn install
25
25
 
26
26
  ### Folder structure
27
27
 
28
- _Work in progress._
28
+ `@grafana/create-plugin` consists of the following folder structure:
29
+
30
+ ```js
31
+ ├── src // Executable code
32
+ │ ├── bin // the entrypoint file
33
+ │ ├── commands // Code that runs commands
34
+ │ └── utils // Utilities used by commands
35
+ └── templates // Plop templates
36
+ ├── _partials // Composable parts of a template
37
+ ├── app // Templates specific to scaffolding an app plugin
38
+ ├── backend // Templates specific to scaffolding backend plugin code
39
+ ├── common // Common templates used by all plugin types (e.g. tooling config files)
40
+ ├── datasource // Templates specific to scaffolding a datasource plugin
41
+ ├── github // Templates for github workflows
42
+ └── panel // Templates specific to scaffolding a panel plugin
43
+ ```
29
44
 
30
45
  ## Development
31
46
 
32
- ### Conventions
47
+ There are a collection of [commands](#commmands) to assist with developing `create-plugin`. Please read the main [contributing guide](../../CONTRIBUTING.md) before contributing any code changes to the project.
33
48
 
34
- _Work in progress._
49
+ ### Commmands
35
50
 
36
- ### Developing the templates
51
+ Below are the main commands used for developing `create-plugin`. They can be run by either `yarn workspace @grafana/create-plugin run <name_of_command>` or navigating to `packages/create-plugin` and running the command directly as detailed below.
37
52
 
38
- _Work in progress._
53
+ ```shell
54
+ yarn build # used to build @grafana/create-plugin
55
+ ```
39
56
 
40
- ### Submitting a Pull Request
57
+ ```shell
58
+ yarn dev # watches for changes to files and rebuilds @grafana/create-plugin automatically
59
+ ```
60
+
61
+ ```shell
62
+ yarn dev-app # watches for changes and scaffolds an app plugin (in ./generated) for developing app configs
63
+ ```
64
+
65
+ ```shell
66
+ yarn dev-panel # watches for changes and scaffolds an app plugin (in ./generated) for developing panel configs
67
+ ```
68
+
69
+ ```shell
70
+ yarn dev-datasource # watches for changes and scaffolds an app plugin (in ./generated) for developing datasource configs
71
+ ```
72
+
73
+ ### Conventions
41
74
 
42
75
  _Work in progress._
76
+
77
+ ### Developing the templates
78
+
79
+ The templates are used by Plop to scaffold Grafana plugins. Whilst they appear to be the intended filetype they are infact treated as markdown by Plop when it runs. As such we need to be mindful of syntax and to [escape particular characters](https://handlebarsjs.com/guide/expressions.html#whitespace-control) where necessary. The [github/ci.yml](./templates/github/ci/.github/workflows/ci.yml) file is a good example of this.
package/README.md CHANGED
@@ -4,11 +4,18 @@ Create Grafana plugins with ease.
4
4
 
5
5
  **ToC**
6
6
 
7
- - [Create a new plugin](#create-a-new-plugin)
8
- - [Migrate your existing plugin](#migrate-your-existing-plugin)
9
- - [Update your plugin build config](#update-your-plugin-build-config)
10
- - [Start developing your plugin](#start-developing-your-plugin)
11
- - [Contributing](#contributing)
7
+ - [Grafana / Create Plugin](#grafana--create-plugin)
8
+ - [Create a new plugin](#create-a-new-plugin)
9
+ - [Quick overview](#quick-overview)
10
+ - [`yarn` (1.x)](#yarn-1x)
11
+ - [`yarn` (> 2.x)](#yarn--2x)
12
+ - [`npx`](#npx)
13
+ - [`npm`](#npm)
14
+ - [Migrate your existing plugin](#migrate-your-existing-plugin)
15
+ - [Things to check after migration](#things-to-check-after-migration)
16
+ - [Update your plugin build config](#update-your-plugin-build-config)
17
+ - [Customizing or extending the basic configs](#customizing-or-extending-the-basic-configs)
18
+ - [Contributing](#contributing)
12
19
 
13
20
  **Links**
14
21
 
@@ -16,46 +23,39 @@ Create Grafana plugins with ease.
16
23
  - [Plugin migration guide](https://grafana.com/docs/grafana/latest/developers/plugins/migration-guide/)
17
24
 
18
25
  **`@grafana/create-plugin`** works on macOS, Windows and Linux.<br />
19
- If something doesnt work, please [file an issue](https://github.com/grafana/create-plugin/issues/new).<br />
20
- If you have questions or need help, please ask in [GitHub Discussions](https://github.com/grafana/create-plugin/discussions).
26
+ If something doesn't work, please [file an issue](https://github.com/grafana/plugin-tools/issues/new).<br />
27
+ If you have questions or need help, please ask in [GitHub Discussions](https://github.com/grafana/plugin-tools/discussions).
21
28
 
22
29
  ## Create a new plugin
23
30
 
24
31
  ### Quick overview
25
32
 
26
- The following commands scaffold a Grafana plugin inside the `./my-plugin` folder:
33
+ Run the command in the folder where you want to store your plugins. The new plugin will be scaffolded in a sub-directory of the folder where you run the command.
27
34
 
28
- ```bash
29
- mkdir my-plugin && cd my-plugin
30
- yarn create @grafana/plugin
31
- yarn install
32
- yarn dev
33
- ```
35
+ You can run the command with the package manager of your choice:
34
36
 
35
- Alterntives:
36
-
37
- #### [`npx`](https://github.com/npm/npx)
37
+ #### [`yarn`](https://classic.yarnpkg.com/blog/2017/05/12/introducing-yarn/) (1.x)
38
38
 
39
39
  ```bash
40
- npx @grafana/create-plugin
40
+ yarn create @grafana/plugin
41
41
  ```
42
42
 
43
- #### [`npm`](https://docs.npmjs.com/cli/v7/commands/npm-init)
43
+ #### [`yarn`](https://yarnpkg.com/cli/dlx) (> 2.x)
44
44
 
45
45
  ```bash
46
- npm init @grafana/plugin
46
+ yarn dlx @grafana/create-plugin
47
47
  ```
48
48
 
49
- #### [`yarn`](https://classic.yarnpkg.com/blog/2017/05/12/introducing-yarn/) (1.x)
49
+ #### [`npx`](https://github.com/npm/npx)
50
50
 
51
51
  ```bash
52
- yarn create @grafana/plugin
52
+ npx @grafana/create-plugin
53
53
  ```
54
54
 
55
- #### [`yarn`](https://yarnpkg.com/cli/dlx) (> 2.x)
55
+ #### [`npm`](https://docs.npmjs.com/cli/v7/commands/npm-init)
56
56
 
57
57
  ```bash
58
- yarn dlx @grafana/create-plugin
58
+ npm init @grafana/plugin
59
59
  ```
60
60
 
61
61
  ---
@@ -97,47 +97,10 @@ npx @grafana/create-plugin update
97
97
 
98
98
  ---
99
99
 
100
- ## Start developing your plugin
101
-
102
- We have put together the following dev scripts for you, so you can start coding right away:
103
-
104
- #### `yarn dev`
105
-
106
- Starts the build in watch mode.
107
- The build output (plugin bundle) is going to be exported to `./dist`.
108
-
109
- #### `yarn build`
110
-
111
- Creates a production build for your plugin.
112
- The build output is going to be exported to `./dist`.
113
-
114
- #### `yarn test`
115
-
116
- Runs the tests under `./src` directory in watch mode.
117
- Give your test files a `.test.tsx` or `.test.ts` extension.
118
-
119
- #### `yarn test:ci`
120
-
121
- Runs the tests and returns with either a zero or a non-zero exit status.
122
-
123
- #### `yarn typecheck`
124
-
125
- Runs the Typescript compiler against the codebase in a dry-run mode to highlight any type errors or warnings.
126
-
127
- #### `yarn lint`
128
-
129
- Runs ESLint against the codebase.
130
-
131
- #### `yarn lint:fix`
132
-
133
- Runs ESLint against the codebase and attempts to fix the simpler errors.
134
-
135
- ---
136
-
137
100
  ## Customizing or extending the basic configs
138
101
 
139
102
  You can read more about customizing or extending the basic configuration [here](templates/common/.config/README.md)
140
103
 
141
104
  ## Contributing
142
105
 
143
- We are always grateful for contribution! See the [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.
106
+ We are always grateful for contributions! See the [CONTRIBUTING.md](../CONTRIBUTING.md) for more information.
@@ -61,7 +61,8 @@ function default_1(plop) {
61
61
  name: 'hasBackend',
62
62
  type: 'confirm',
63
63
  message: 'Do you want a backend part of your plugin?',
64
- "default": false
64
+ "default": false,
65
+ when: function (answers) { return answers.pluginType !== constants_1.PLUGIN_TYPES.panel; }
65
66
  },
66
67
  {
67
68
  name: 'hasGithubWorkflows',
@@ -48,7 +48,7 @@ var update = function () { return __awaiter(void 0, void 0, void 0, function ()
48
48
  case 0:
49
49
  _a.trys.push([0, 5, , 6]);
50
50
  (0, utils_console_1.printMessage)(constants_1.TEXT.updateCommandWarning);
51
- return [4, (0, utils_console_1.confirmPrompt)(constants_1.TEXT.overrideFilesPrompt)];
51
+ return [4, (0, utils_console_1.confirmPrompt)(constants_1.TEXT.updateConfigPrompt)];
52
52
  case 1:
53
53
  if (_a.sent()) {
54
54
  (0, utils_templates_1.compileTemplateFiles)(constants_1.UDPATE_CONFIG.filesToOverride, (0, utils_templates_1.getTemplateData)());
package/dist/constants.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  exports.__esModule = true;
6
- exports.TEXT = exports.UDPATE_CONFIG = exports.MIGRATION_CONFIG = exports.EXTRA_TEMPLATE_VARIABLES = exports.PLUGIN_TYPES = exports.TEMPLATE_PATHS = exports.PARTIALS_DIR = exports.TEMPLATES_DIR = exports.DIST_DIR = exports.EXPORT_PATH_PREFIX = exports.DEV_EXPORT_DIR = exports.IS_DEV = void 0;
6
+ exports.TEXT = exports.UDPATE_CONFIG = exports.MIGRATION_CONFIG = exports.EXTRA_TEMPLATE_VARIABLES = exports.PLUGIN_TYPES = exports.TEMPLATE_PATHS = exports.FIXTURES_PATH = exports.PARTIALS_DIR = exports.TEMPLATES_DIR = exports.DIST_DIR = exports.EXPORT_PATH_PREFIX = exports.DEV_EXPORT_DIR = exports.IS_DEV = void 0;
7
7
  var path_1 = __importDefault(require("path"));
8
8
  exports.IS_DEV = process.env.CREATE_PLUGIN_DEV !== undefined;
9
9
  exports.DEV_EXPORT_DIR = path_1["default"].join(__dirname, '..', 'generated');
@@ -11,6 +11,7 @@ exports.EXPORT_PATH_PREFIX = exports.IS_DEV ? exports.DEV_EXPORT_DIR : process.c
11
11
  exports.DIST_DIR = path_1["default"].join(__dirname, 'dist');
12
12
  exports.TEMPLATES_DIR = path_1["default"].join(__dirname, '..', 'templates');
13
13
  exports.PARTIALS_DIR = path_1["default"].join(exports.TEMPLATES_DIR, '_partials');
14
+ exports.FIXTURES_PATH = path_1["default"].join(__dirname, '..', 'fixtures');
14
15
  exports.TEMPLATE_PATHS = {
15
16
  app: path_1["default"].join(exports.TEMPLATES_DIR, 'app'),
16
17
  backend: path_1["default"].join(exports.TEMPLATES_DIR, 'backend'),
@@ -25,6 +26,7 @@ var PLUGIN_TYPES;
25
26
  PLUGIN_TYPES["app"] = "app";
26
27
  PLUGIN_TYPES["panel"] = "panel";
27
28
  PLUGIN_TYPES["datasource"] = "datasource";
29
+ PLUGIN_TYPES["secretsmanager"] = "secretsmanager";
28
30
  })(PLUGIN_TYPES = exports.PLUGIN_TYPES || (exports.PLUGIN_TYPES = {}));
29
31
  exports.EXTRA_TEMPLATE_VARIABLES = {
30
32
  grafanaVersion: '9.1.2'
@@ -64,11 +66,12 @@ exports.TEXT = {
64
66
  removeNpmDependenciesPrompt: '**Would you like to remove the following possibly unnecessary NPM dependencies?**',
65
67
  removeNpmDependenciesSuccess: 'Unnecessary NPM dependencies removed successfully.',
66
68
  removeNpmDependenciesAborted: 'No NPM dependencies have been removed.',
69
+ updateConfigPrompt: "**Would you like to update the project's configuration?**",
67
70
  updateNpmScriptsPrompt: '**Would you like to update the `{ scripts }` in your `package.json`?**',
68
71
  updateNpmScriptsSuccess: 'NPM scripts updated successfully.',
69
72
  updateNpmScriptsAborted: 'No NPM scripts have been added or updated.',
70
73
  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.',
71
- migrationCommandSuccess: '**Done.**\nIf you have any questions please open an issue/discussion in https://github.com/grafana/create-plugin.',
74
+ migrationCommandSuccess: '**Done.**\nIf you have any questions please open an issue/discussion in https://github.com/grafana/plugin-tools.',
72
75
  updateCommandWarning: '**⚠️ Warning!**\nThis is going to update files under the `.config/` folder.\nMake sure to commit your changes before running this script.',
73
- updateCommandSuccess: '**Done.**\nIf you have any questions please open an issue/discussion in https://github.com/grafana/create-plugin.'
76
+ updateCommandSuccess: '**Done.**\nIf you have any questions please open an issue/discussion in https://github.com/grafana/plugin-tools.'
74
77
  };
@@ -8,19 +8,19 @@ describe('Handlebars helpers', function () {
8
8
  var pluginName = 'my-plugin';
9
9
  var orgName = 'my-org';
10
10
  var actual = (0, utils_handlebars_1.normalizeId)(pluginName, orgName, type);
11
- expect(actual).toEqual("my-org-my-plugin-".concat(type));
11
+ expect(actual).toEqual("myorg-myplugin-".concat(type));
12
12
  });
13
13
  test.each([constants_1.PLUGIN_TYPES.app, constants_1.PLUGIN_TYPES.datasource, constants_1.PLUGIN_TYPES.panel])('should not duplicate the type if it is already present', function (type) {
14
14
  var pluginName = "my-plugin-".concat(type);
15
15
  var orgName = 'my-org';
16
16
  var actual = (0, utils_handlebars_1.normalizeId)(pluginName, orgName, type);
17
- expect(actual).toEqual("my-org-my-plugin-".concat(type));
17
+ expect(actual).toEqual("myorg-myplugin-".concat(type));
18
18
  });
19
19
  test.each([constants_1.PLUGIN_TYPES.app, constants_1.PLUGIN_TYPES.datasource, constants_1.PLUGIN_TYPES.panel])('should not duplicate the type if it is already present (no dash before type)', function (type) {
20
20
  var pluginName = "my-plugin".concat(type);
21
21
  var orgName = 'my-org';
22
22
  var actual = (0, utils_handlebars_1.normalizeId)(pluginName, orgName, type);
23
- expect(actual).toEqual("my-org-my-plugin-".concat(type));
23
+ expect(actual).toEqual("myorg-myplugin-".concat(type));
24
24
  });
25
25
  });
26
26
  });
@@ -5,7 +5,7 @@ var utils_plugin_1 = require("../utils.plugin");
5
5
  describe('Utils / Plugins', function () {
6
6
  describe('getPluginJson()', function () {
7
7
  test('should return the parsed plugin JSON if the file exits', function () {
8
- var srcDir = "".concat(constants_1.TEMPLATE_PATHS.app, "/src");
8
+ var srcDir = "".concat(constants_1.FIXTURES_PATH, "/test-template/src");
9
9
  var pluginJson = (0, utils_plugin_1.getPluginJson)(srcDir);
10
10
  expect(pluginJson).toBeDefined();
11
11
  expect(pluginJson.type).toBe('app');
@@ -39,8 +39,10 @@ var ifEq = function (a, b, options) {
39
39
  exports.ifEq = ifEq;
40
40
  var normalizeId = function (pluginName, orgName, type) {
41
41
  var re = new RegExp("-?".concat(type, "$"), 'i');
42
- var newPluginName = pluginName.replace(re, '');
43
- return changeCase.paramCase(orgName) + '-' + changeCase.paramCase(newPluginName) + "-".concat(type);
42
+ var nameRegex = new RegExp('[^0-9a-z]', 'g');
43
+ var newPluginName = pluginName.replace(re, '').replace(nameRegex, '');
44
+ var newOrgName = orgName.replace(nameRegex, '');
45
+ return changeCase.lowerCase(newOrgName) + '-' + changeCase.lowerCase(newPluginName) + "-".concat(type);
44
46
  };
45
47
  exports.normalizeId = normalizeId;
46
48
  function registerHandlebarsHelpers() {
@@ -0,0 +1,35 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/grafana/grafana/master/docs/sources/developers/plugins/plugin.schema.json",
3
+ "type": "app",
4
+ "name": "{{ titleCase pluginName }}",
5
+ "id": "{{ normalize_id pluginName orgName 'app' }}",
6
+ "backend": true,
7
+ "executable": "gpx_{{ snakeCase pluginName }}",
8
+ "info": {
9
+ "description": "{{ sentenceCase pluginDescription }}",
10
+ "author": {
11
+ "name": "{{ sentenceCase orgName }}"
12
+ },
13
+ "logos": {
14
+ "small": "img/logo.svg",
15
+ "large": "img/logo.svg"
16
+ },
17
+ "screenshots": [],
18
+ "version": "%VERSION%",
19
+ "updated": "%TODAY%"
20
+ },
21
+ "includes": [
22
+ {
23
+ "type": "page",
24
+ "name": "Default",
25
+ "path": "/a/%PLUGIN_ID%",
26
+ "role": "Admin",
27
+ "addToNav": true,
28
+ "defaultNav": true
29
+ }
30
+ ],
31
+ "dependencies": {
32
+ "grafanaDependency": ">={{ grafanaVersion }}",
33
+ "plugins": []
34
+ }
35
+ }
package/jest.config.js CHANGED
@@ -1,20 +1,5 @@
1
+ const sharedConfig = require('../../jest.config.base');
1
2
  module.exports = {
2
- testMatch: ['<rootDir>/src/**/*.{spec,test,jest}.{js,jsx,ts,tsx}'],
3
- transform: {
4
- '^.+\\.(t|j)sx?$': [
5
- '@swc/jest',
6
- {
7
- sourceMaps: true,
8
- jsc: {
9
- parser: {
10
- syntax: 'typescript',
11
- tsx: true,
12
- decorators: false,
13
- dynamicImport: true,
14
- },
15
- },
16
- },
17
- ],
18
- },
3
+ ...sharedConfig,
19
4
  modulePathIgnorePatterns: ['<rootDir>/templates/'],
20
5
  };
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@grafana/create-plugin",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "main": "index.js",
5
- "repository": "git@github.com:grafana/create-plugin.git",
5
+ "repository": {
6
+ "directory": "packages/create-plugin",
7
+ "url": "git@github.com:grafana/plugin-tools.git"
8
+ },
6
9
  "author": "Grafana",
7
10
  "license": "Apache-2.0",
8
11
  "bin": "./dist/bin/run.js",
@@ -14,45 +17,30 @@
14
17
  "dev-app": "CREATE_PLUGIN_DEV=true nodemon --exec 'yarn generate-app'",
15
18
  "dev-panel": "CREATE_PLUGIN_DEV=true nodemon --exec 'yarn generate-panel'",
16
19
  "dev-datasource": "CREATE_PLUGIN_DEV=true nodemon --exec 'yarn generate-datasource'",
17
- "generate-app": "tsc && yarn clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js -- create-plugin 'Sample app' 'sample-org' 'This is a sample app.' 'app' 't' 't' 't'",
18
- "generate-panel": "tsc && yarn clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js -- create-plugin 'Sample panel' 'sample-org' 'This is a sample panel.' 'panel' 't' 't' 't'",
19
- "generate-datasource": "tsc && yarn clean-generated && CREATE_PLUGIN_DEV=true node ./dist/bin/run.js -- create-plugin 'Sample datasource' 'sample-org' 'This is a sample datasource.' 'datasource' 't' 't' 't'",
20
- "lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx ./src",
20
+ "generate-app": "tsc && yarn clean-generated && CREATE_PLUGIN_DEV=true node ./scripts/generate-app.js",
21
+ "generate-panel": "tsc && yarn clean-generated && CREATE_PLUGIN_DEV=true node ./scripts/generate-panel.js",
22
+ "generate-datasource": "tsc && yarn clean-generated && CREATE_PLUGIN_DEV=true node ./scripts/generate-datasource.js",
23
+ "lint": "eslint --cache --ext .js,.jsx,.ts,.tsx ./src",
21
24
  "lint:fix": "yarn lint --fix",
22
- "test": "jest --watch",
25
+ "test": "jest",
23
26
  "typecheck": "tsc --noEmit"
24
27
  },
25
28
  "dependencies": {
26
- "@grafana/tsconfig": "^1.2.0-rc1",
27
- "@swc/core": "^1.2.162",
28
29
  "@types/minimist": "^1.2.2",
29
30
  "@types/mkdirp": "^1.0.2",
30
31
  "@types/semver": "^7.3.9",
31
32
  "enquirer": "^2.3.6",
32
33
  "glob": "^7.1.7",
33
- "jest": "^27.5.1",
34
- "marked-terminal": "^5.1.1",
35
34
  "marked": "^4.0.12",
35
+ "marked-terminal": "^5.1.1",
36
36
  "mkdirp": "^1.0.4",
37
37
  "plop": "^2.7.4",
38
38
  "semver": "^7.3.5"
39
39
  },
40
40
  "devDependencies": {
41
- "@grafana/eslint-config": "^2.5.0",
42
- "@swc/jest": "^0.2.20",
43
41
  "@types/glob": "^7.0.0",
44
- "@types/jest": "^27.4.1",
45
- "@typescript-eslint/eslint-plugin": "^4.33.0",
46
- "@typescript-eslint/parser": "^4.33.0",
47
- "eslint": "^7.32.0",
48
- "eslint-config-prettier": "^8.3.0",
49
- "eslint-plugin-jsdoc": "^36.1.0",
50
- "eslint-plugin-prettier": "^4.0.0",
51
42
  "eslint-plugin-react": "^7.26.1",
52
- "eslint-plugin-react-hooks": "^4.2.0",
53
- "nodemon": "^2.0.13",
54
- "tsc-watch": "^4.5.0",
55
- "typescript": "^4.4.3"
43
+ "eslint-plugin-react-hooks": "^4.2.0"
56
44
  },
57
45
  "nodemonConfig": {
58
46
  "watch": [
@@ -0,0 +1,24 @@
1
+ const nodePlop = require('node-plop');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ async function generatePanel() {
6
+ const plopFile = path.join(__dirname, '../dist/commands/generate.plopfile.js');
7
+ if (!fs.existsSync(plopFile)) {
8
+ console.error('Run build first');
9
+ process.exit(1);
10
+ }
11
+ const plop = await nodePlop(plopFile);
12
+ const generator = plop.getGenerator('create-plugin');
13
+ await generator.runActions({
14
+ pluginName: 'my-plugin',
15
+ orgName: 'my-org',
16
+ pluginDescription: 'Auto-generated app',
17
+ pluginType: 'app',
18
+ hasBackend: false,
19
+ hasGithubWorkflows: true,
20
+ hasGithubLevitateWorkflow: true,
21
+ });
22
+ }
23
+
24
+ generatePanel();
@@ -0,0 +1,24 @@
1
+ const nodePlop = require('node-plop');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ async function generatePanel() {
6
+ const plopFile = path.join(__dirname, '../dist/commands/generate.plopfile.js');
7
+ if (!fs.existsSync(plopFile)) {
8
+ console.error('Run build first');
9
+ process.exit(1);
10
+ }
11
+ const plop = await nodePlop(plopFile);
12
+ const generator = plop.getGenerator('create-plugin');
13
+ await generator.runActions({
14
+ pluginName: 'my-plugin',
15
+ orgName: 'my-org',
16
+ pluginDescription: 'Auto-generated datasource',
17
+ pluginType: 'datasource',
18
+ hasBackend: true,
19
+ hasGithubWorkflows: true,
20
+ hasGithubLevitateWorkflow: true,
21
+ });
22
+ }
23
+
24
+ generatePanel();
@@ -0,0 +1,23 @@
1
+ const nodePlop = require('node-plop');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ async function generatePanel() {
6
+ const plopFile = path.join(__dirname, '../dist/commands/generate.plopfile.js');
7
+ if (!fs.existsSync(plopFile)) {
8
+ console.error('Run build first');
9
+ process.exit(1);
10
+ }
11
+ const plop = await nodePlop(plopFile);
12
+ const generator = plop.getGenerator('create-plugin');
13
+ await generator.runActions({
14
+ pluginName: 'my-plugin',
15
+ orgName: 'my-org',
16
+ pluginDescription: 'Auto-generated panel',
17
+ pluginType: 'panel',
18
+ hasGithubWorkflows: true,
19
+ hasGithubLevitateWorkflow: true,
20
+ });
21
+ }
22
+
23
+ generatePanel();
@@ -69,6 +69,7 @@ export default function (plop: NodePlopAPI) {
69
69
  type: 'confirm',
70
70
  message: 'Do you want a backend part of your plugin?',
71
71
  default: false,
72
+ when: (answers: CliArgs) => answers.pluginType !== PLUGIN_TYPES.panel,
72
73
  },
73
74
  {
74
75
  name: 'hasGithubWorkflows',
@@ -16,7 +16,7 @@ export const update = async () => {
16
16
 
17
17
  // 1. Add / update configuration files
18
18
  // -----------------------------------
19
- if (await confirmPrompt(TEXT.overrideFilesPrompt)) {
19
+ if (await confirmPrompt(TEXT.updateConfigPrompt)) {
20
20
  compileTemplateFiles(UDPATE_CONFIG.filesToOverride, getTemplateData());
21
21
  printSuccessMessage(TEXT.overrideFilesSuccess);
22
22
  } else {
package/src/constants.ts CHANGED
@@ -14,6 +14,8 @@ export const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
14
14
  // (they won't be copied over to the generated folder as they are)
15
15
  export const PARTIALS_DIR = path.join(TEMPLATES_DIR, '_partials');
16
16
 
17
+ export const FIXTURES_PATH = path.join(__dirname, '..', 'fixtures');
18
+
17
19
  export const TEMPLATE_PATHS: Record<string, string> = {
18
20
  app: path.join(TEMPLATES_DIR, 'app'),
19
21
  backend: path.join(TEMPLATES_DIR, 'backend'),
@@ -28,6 +30,7 @@ export enum PLUGIN_TYPES {
28
30
  app = 'app',
29
31
  panel = 'panel',
30
32
  datasource = 'datasource',
33
+ secretsmanager = 'secretsmanager',
31
34
  }
32
35
 
33
36
  // This gets merged into variables coming from user prompts (when using Plop) or any other dynamic variables,
@@ -87,13 +90,15 @@ export const TEXT = {
87
90
  removeNpmDependenciesSuccess: 'Unnecessary NPM dependencies removed successfully.',
88
91
  removeNpmDependenciesAborted: 'No NPM dependencies have been removed.',
89
92
 
93
+ updateConfigPrompt: "**Would you like to update the project's configuration?**",
94
+
90
95
  updateNpmScriptsPrompt: '**Would you like to update the `{ scripts }` in your `package.json`?**',
91
96
  updateNpmScriptsSuccess: 'NPM scripts updated successfully.',
92
97
  updateNpmScriptsAborted: 'No NPM scripts have been added or updated.',
93
98
 
94
99
  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.',
100
+ migrationCommandSuccess: '**Done.**\nIf you have any questions please open an issue/discussion in https://github.com/grafana/plugin-tools.',
96
101
 
97
102
  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.',
103
+ updateCommandSuccess: '**Done.**\nIf you have any questions please open an issue/discussion in https://github.com/grafana/plugin-tools.',
99
104
  }
@@ -9,7 +9,7 @@ describe('Handlebars helpers', () => {
9
9
  const pluginName = 'my-plugin';
10
10
  const orgName = 'my-org';
11
11
  const actual = normalizeId(pluginName, orgName, type);
12
- expect(actual).toEqual(`my-org-my-plugin-${type}`);
12
+ expect(actual).toEqual(`myorg-myplugin-${type}`);
13
13
  }
14
14
  );
15
15
  test.each([PLUGIN_TYPES.app, PLUGIN_TYPES.datasource, PLUGIN_TYPES.panel])(
@@ -18,7 +18,7 @@ describe('Handlebars helpers', () => {
18
18
  const pluginName = `my-plugin-${type}`;
19
19
  const orgName = 'my-org';
20
20
  const actual = normalizeId(pluginName, orgName, type);
21
- expect(actual).toEqual(`my-org-my-plugin-${type}`);
21
+ expect(actual).toEqual(`myorg-myplugin-${type}`);
22
22
  }
23
23
  );
24
24
 
@@ -28,7 +28,7 @@ describe('Handlebars helpers', () => {
28
28
  const pluginName = `my-plugin${type}`;
29
29
  const orgName = 'my-org';
30
30
  const actual = normalizeId(pluginName, orgName, type);
31
- expect(actual).toEqual(`my-org-my-plugin-${type}`);
31
+ expect(actual).toEqual(`myorg-myplugin-${type}`);
32
32
  }
33
33
  );
34
34
  });
@@ -1,10 +1,10 @@
1
- import { TEMPLATE_PATHS } from '../../constants';
1
+ import { FIXTURES_PATH, TEMPLATE_PATHS } from '../../constants';
2
2
  import { getPluginJson } from '../utils.plugin';
3
3
 
4
4
  describe('Utils / Plugins', () => {
5
5
  describe('getPluginJson()', () => {
6
6
  test('should return the parsed plugin JSON if the file exits', () => {
7
- const srcDir = `${TEMPLATE_PATHS.app}/src`;
7
+ const srcDir = `${FIXTURES_PATH}/test-template/src`;
8
8
  const pluginJson = getPluginJson(srcDir);
9
9
 
10
10
  expect(pluginJson).toBeDefined();
@@ -12,8 +12,11 @@ export const ifEq = (a: any, b: any, options: HelperOptions) => {
12
12
 
13
13
  export const normalizeId = (pluginName: string, orgName: string, type: PLUGIN_TYPES) => {
14
14
  const re = new RegExp(`-?${type}$`, 'i');
15
- const newPluginName = pluginName.replace(re, '');
16
- return changeCase.paramCase(orgName) + '-' + changeCase.paramCase(newPluginName) + `-${type}`;
15
+ const nameRegex = new RegExp('[^0-9a-z]', 'g');
16
+
17
+ const newPluginName = pluginName.replace(re, '').replace(nameRegex, '');
18
+ const newOrgName = orgName.replace(nameRegex, '');
19
+ return changeCase.lowerCase(newOrgName) + '-' + changeCase.lowerCase(newPluginName) + `-${type}`;
17
20
  };
18
21
 
19
22
  // Needed when we are rendering the templates outside of the context of Plop but still would like to support the same helpers
@@ -2,8 +2,11 @@
2
2
  "$schema": "https://raw.githubusercontent.com/grafana/grafana/master/docs/sources/developers/plugins/plugin.schema.json",
3
3
  "type": "app",
4
4
  "name": "{{ titleCase pluginName }}",
5
- "id": "{{ normalize_id pluginName orgName 'app' }}",
5
+ "id": "{{ normalize_id pluginName orgName 'app' }}",{{#if hasBackend}}
6
+ "backend": true,
7
+ "executable": "gpx_{{ snakeCase pluginName }}",{{/if}}
6
8
  "info": {
9
+ "keywords": ["app"],
7
10
  "description": "{{ sentenceCase pluginDescription }}",
8
11
  "author": {
9
12
  "name": "{{ sentenceCase orgName }}"