@grafana/create-plugin 6.3.0-canary.2320.19638409321.0 → 6.3.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.
@@ -1,81 +0,0 @@
1
- import * as v from 'valibot';
2
-
3
- import type { Context } from '../../../context.js';
4
- import { additionsDebug } from '../../../utils.js';
5
- import { updateDockerCompose, updatePluginJson, createI18nextConfig } from './config-updates.js';
6
- import { addI18nInitialization, createLoadResourcesFile } from './code-generation.js';
7
- import { updateEslintConfig, addI18nDependency, addSemverDependency, addI18nextCli } from './tooling.js';
8
- import { checkNeedsBackwardCompatibility, createLocaleFiles } from './utils.js';
9
-
10
- /**
11
- * I18n addition schema using Valibot
12
- * Adds internationalization support to a plugin
13
- */
14
- export const schema = v.object(
15
- {
16
- locales: v.pipe(
17
- v.union([v.string(), v.array(v.string())]),
18
- v.transform((input) => {
19
- // Handle both string (from CLI) and array (from tests)
20
- return typeof input === 'string' ? input.split(',').map((s) => s.trim()) : input;
21
- }),
22
- v.array(
23
- v.pipe(
24
- v.string(),
25
- v.regex(/^[a-z]{2}-[A-Z]{2}$/, 'Locale must be in format xx-XX (e.g., en-US, es-ES, sv-SE)')
26
- ),
27
- 'Please provide a comma-separated list of all supported locales, e.g., "en-US,es-ES,sv-SE"'
28
- ),
29
- v.minLength(1, 'Please provide a comma-separated list of all supported locales, e.g., "en-US,es-ES,sv-SE"')
30
- ),
31
- },
32
- 'Please provide a comma-separated list of all supported locales, e.g., "en-US,es-ES,sv-SE"'
33
- );
34
-
35
- type I18nOptions = v.InferOutput<typeof schema>;
36
-
37
- export default function i18nAddition(context: Context, options: I18nOptions): Context {
38
- const { locales } = options;
39
-
40
- additionsDebug('Adding i18n support with locales:', locales);
41
-
42
- // Determine if we need backward compatibility (Grafana < 12.1.0)
43
- const needsBackwardCompatibility = checkNeedsBackwardCompatibility(context);
44
- additionsDebug('Needs backward compatibility:', needsBackwardCompatibility);
45
-
46
- // 1. Update docker-compose.yaml with feature toggle
47
- updateDockerCompose(context);
48
-
49
- // 2. Update plugin.json with languages and grafanaDependency
50
- updatePluginJson(context, locales, needsBackwardCompatibility);
51
-
52
- // 3. Create locale folders and files
53
- createLocaleFiles(context, locales);
54
-
55
- // 4. Add @grafana/i18n dependency
56
- addI18nDependency(context);
57
-
58
- // 5. Add semver dependency for backward compatibility
59
- if (needsBackwardCompatibility) {
60
- addSemverDependency(context);
61
- }
62
-
63
- // 6. Update eslint.config.mjs if needed
64
- updateEslintConfig(context);
65
-
66
- // 7. Add i18n initialization to module file
67
- addI18nInitialization(context, needsBackwardCompatibility);
68
-
69
- // 8. Create loadResources.ts for backward compatibility
70
- if (needsBackwardCompatibility) {
71
- createLoadResourcesFile(context);
72
- }
73
-
74
- // 9. Add i18next-cli as dev dependency and add script
75
- addI18nextCli(context);
76
-
77
- // 10. Create i18next.config.ts
78
- createI18nextConfig(context);
79
-
80
- return context;
81
- }
@@ -1,146 +0,0 @@
1
- import * as recast from 'recast';
2
- import * as babelParser from 'recast/parsers/babel-ts.js';
3
-
4
- import type { Context } from '../../../context.js';
5
- import { addDependenciesToPackageJson, additionsDebug } from '../../../utils.js';
6
-
7
- const { builders } = recast.types;
8
-
9
- export function addI18nDependency(context: Context): void {
10
- addDependenciesToPackageJson(context, { '@grafana/i18n': '^12.2.2' }, {});
11
- additionsDebug('Added @grafana/i18n dependency version ^12.2.2');
12
- }
13
-
14
- export function addSemverDependency(context: Context): void {
15
- // Add semver as regular dependency and @types/semver as dev dependency
16
- addDependenciesToPackageJson(context, { semver: '^7.6.0' }, { '@types/semver': '^7.5.0' });
17
- additionsDebug('Added semver dependency');
18
- }
19
-
20
- export function addI18nextCli(context: Context): void {
21
- // Add i18next-cli as dev dependency
22
- addDependenciesToPackageJson(context, {}, { 'i18next-cli': '^1.1.1' });
23
-
24
- // Add i18n-extract script to package.json
25
- const packageJsonRaw = context.getFile('package.json');
26
- if (!packageJsonRaw) {
27
- return;
28
- }
29
-
30
- try {
31
- const packageJson = JSON.parse(packageJsonRaw);
32
-
33
- if (!packageJson.scripts) {
34
- packageJson.scripts = {};
35
- }
36
-
37
- // Defensive: only add if not already present
38
- if (!packageJson.scripts['i18n-extract']) {
39
- packageJson.scripts['i18n-extract'] = 'i18next-cli extract --sync-primary';
40
- context.updateFile('package.json', JSON.stringify(packageJson, null, 2));
41
- additionsDebug('Added i18n-extract script to package.json');
42
- } else {
43
- additionsDebug('i18n-extract script already exists, skipping');
44
- }
45
- } catch (error) {
46
- additionsDebug('Error adding i18n-extract script:', error);
47
- }
48
- }
49
-
50
- export function updateEslintConfig(context: Context): void {
51
- if (!context.doesFileExist('eslint.config.mjs')) {
52
- additionsDebug('eslint.config.mjs not found, skipping');
53
- return;
54
- }
55
-
56
- const eslintConfigRaw = context.getFile('eslint.config.mjs');
57
- if (!eslintConfigRaw) {
58
- return;
59
- }
60
-
61
- // Defensive: check if @grafana/i18n eslint plugin is already configured
62
- if (eslintConfigRaw.includes('@grafana/i18n/eslint-plugin')) {
63
- additionsDebug('ESLint i18n rule already configured');
64
- return;
65
- }
66
-
67
- try {
68
- const ast = recast.parse(eslintConfigRaw, {
69
- parser: babelParser,
70
- });
71
-
72
- // Find the import section and add the plugin import
73
- const imports = ast.program.body.filter((node: any) => node.type === 'ImportDeclaration');
74
- const lastImport = imports[imports.length - 1];
75
-
76
- if (lastImport) {
77
- const pluginImport = builders.importDeclaration(
78
- [builders.importDefaultSpecifier(builders.identifier('grafanaI18nPlugin'))],
79
- builders.literal('@grafana/i18n/eslint-plugin')
80
- );
81
-
82
- const lastImportIndex = ast.program.body.indexOf(lastImport);
83
- ast.program.body.splice(lastImportIndex + 1, 0, pluginImport);
84
- }
85
-
86
- // Find the defineConfig array and add the plugin config
87
- recast.visit(ast, {
88
- visitCallExpression(path: any) {
89
- if (path.node.callee.name === 'defineConfig' && path.node.arguments[0]?.type === 'ArrayExpression') {
90
- const configArray = path.node.arguments[0];
91
-
92
- // Add the grafana i18n config object
93
- const i18nConfig = builders.objectExpression([
94
- builders.property('init', builders.identifier('name'), builders.literal('grafana/i18n-rules')),
95
- builders.property(
96
- 'init',
97
- builders.identifier('plugins'),
98
- builders.objectExpression([
99
- builders.property('init', builders.literal('@grafana/i18n'), builders.identifier('grafanaI18nPlugin')),
100
- ])
101
- ),
102
- builders.property(
103
- 'init',
104
- builders.identifier('rules'),
105
- builders.objectExpression([
106
- builders.property(
107
- 'init',
108
- builders.literal('@grafana/i18n/no-untranslated-strings'),
109
- builders.arrayExpression([
110
- builders.literal('error'),
111
- builders.objectExpression([
112
- builders.property(
113
- 'init',
114
- builders.identifier('calleesToIgnore'),
115
- builders.arrayExpression([builders.literal('^css$'), builders.literal('use[A-Z].*')])
116
- ),
117
- ]),
118
- ])
119
- ),
120
- builders.property(
121
- 'init',
122
- builders.literal('@grafana/i18n/no-translation-top-level'),
123
- builders.literal('error')
124
- ),
125
- ])
126
- ),
127
- ]);
128
-
129
- configArray.elements.push(i18nConfig);
130
- }
131
- this.traverse(path);
132
- },
133
- });
134
-
135
- const output = recast.print(ast, {
136
- tabWidth: 2,
137
- trailingComma: true,
138
- lineTerminator: '\n',
139
- }).code;
140
-
141
- context.updateFile('eslint.config.mjs', output);
142
- additionsDebug('Updated eslint.config.mjs with i18n linting rules');
143
- } catch (error) {
144
- additionsDebug('Error updating eslint.config.mjs:', error);
145
- }
146
- }
@@ -1,60 +0,0 @@
1
- import { coerce, gte } from 'semver';
2
-
3
- import type { Context } from '../../../context.js';
4
- import { additionsDebug } from '../../../utils.js';
5
-
6
- export function checkNeedsBackwardCompatibility(context: Context): boolean {
7
- const pluginJsonRaw = context.getFile('src/plugin.json');
8
- if (!pluginJsonRaw) {
9
- return false;
10
- }
11
-
12
- try {
13
- const pluginJson = JSON.parse(pluginJsonRaw);
14
- const currentGrafanaDep = pluginJson.dependencies?.grafanaDependency || '>=11.0.0';
15
- const minVersion = coerce('12.1.0');
16
- const currentVersion = coerce(currentGrafanaDep.replace(/^[><=]+/, ''));
17
-
18
- // If current version is less than 12.1.0, we need backward compatibility
19
- if (currentVersion && minVersion && gte(currentVersion, minVersion)) {
20
- return false; // Already >= 12.1.0, no backward compat needed
21
- }
22
- return true; // < 12.1.0, needs backward compat
23
- } catch (error) {
24
- additionsDebug('Error checking backward compatibility:', error);
25
- return true; // Default to backward compat on error
26
- }
27
- }
28
-
29
- export function createLocaleFiles(context: Context, locales: string[]): void {
30
- // Get plugin ID from plugin.json
31
- const pluginJsonRaw = context.getFile('src/plugin.json');
32
- if (!pluginJsonRaw) {
33
- additionsDebug('Cannot create locale files without plugin.json');
34
- return;
35
- }
36
-
37
- try {
38
- const pluginJson = JSON.parse(pluginJsonRaw);
39
- const pluginId = pluginJson.id;
40
-
41
- if (!pluginId) {
42
- additionsDebug('No plugin ID found in plugin.json');
43
- return;
44
- }
45
-
46
- // Create locale files for each locale (defensive: only if not already present)
47
- for (const locale of locales) {
48
- const localePath = `src/locales/${locale}/${pluginId}.json`;
49
-
50
- if (!context.doesFileExist(localePath)) {
51
- context.addFile(localePath, JSON.stringify({}, null, 2));
52
- additionsDebug(`Created ${localePath}`);
53
- } else {
54
- additionsDebug(`${localePath} already exists, skipping`);
55
- }
56
- }
57
- } catch (error) {
58
- additionsDebug('Error creating locale files:', error);
59
- }
60
- }