diff --git a/.config/README.md b/.config/README.md index d3d6b55..f1ded34 100644 --- a/.config/README.md +++ b/.config/README.md @@ -60,7 +60,7 @@ A common issue found with the current jest config involves importing an npm pack ```javascript process.env.TZ = 'UTC'; -const { grafanaESModules, nodeModulesToTransform } = require('./jest/utils'); +const { grafanaESModules, nodeModulesToTransform } = require('./config/jest/utils'); module.exports = { // Jest configuration provided by Grafana @@ -161,4 +161,4 @@ services: In this example we are assigning the environment variable `GRAFANA_IMAGE` to the build arg `grafana_image` with a default value of `grafana`. This will give you the possibility to set the value while running the docker-compose commands which might be convinent in some scenarios. ---- \ No newline at end of file +--- diff --git a/.config/jest.config.js b/.config/jest.config.js index 027c0ff..3cb011e 100644 --- a/.config/jest.config.js +++ b/.config/jest.config.js @@ -25,7 +25,7 @@ module.exports = { '^.+\\.(t|j)sx?$': [ '@swc/jest', { - sourceMaps: true, + sourceMaps: 'inline', jsc: { parser: { syntax: 'typescript', diff --git a/.config/jest/utils.js b/.config/jest/utils.js index a51d626..1c10884 100644 --- a/.config/jest/utils.js +++ b/.config/jest/utils.js @@ -12,6 +12,8 @@ const nodeModulesToTransform = (moduleNames) => `node_modules\/(?!(${moduleNames // Array of known nested grafana package dependencies that only bundle an ESM version const grafanaESModules = [ + '.pnpm', // Support using pnpm symlinked packages + '@grafana/schema', 'd3', 'd3-color', 'd3-force', @@ -19,10 +21,11 @@ const grafanaESModules = [ 'd3-scale-chromatic', 'ol', 'react-colorful', + 'rxjs', 'uuid', ]; module.exports = { nodeModulesToTransform, - grafanaESModules -} \ No newline at end of file + grafanaESModules, +}; diff --git a/.config/webpack/utils.ts b/.config/webpack/utils.ts index a6776ca..144ea19 100644 --- a/.config/webpack/utils.ts +++ b/.config/webpack/utils.ts @@ -1,10 +1,27 @@ import fs from 'fs'; +import process from 'process'; +import os from 'os'; import path from 'path'; import util from 'util'; -import glob from 'glob'; +import { glob } from 'glob'; import { SOURCE_DIR } from './constants'; -const globAsync = util.promisify(glob); + +export function isWSL() { + if (process.platform !== 'linux') { + return false; + } + + if (os.release().toLowerCase().includes('microsoft')) { + return true; + } + + try { + return fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft'); + } catch { + return false; + } +} export function getPackageJson() { return require(path.resolve(process.cwd(), 'package.json')); @@ -21,11 +38,11 @@ export function hasReadme() { // Support bundling nested plugins by finding all plugin.json files in src directory // then checking for a sibling module.[jt]sx? file. export async function getEntries(): Promise> { - const pluginsJson = await globAsync('**/src/**/plugin.json', { absolute: true }); + const pluginsJson = await glob('**/src/**/plugin.json', { absolute: true }); const plugins = await Promise.all(pluginsJson.map((pluginJson) => { const folder = path.dirname(pluginJson); - return globAsync(`${folder}/module.{ts,tsx,js,jsx}`, { absolute: true }); + return glob(`${folder}/module.{ts,tsx,js,jsx}`, { absolute: true }); }) ); diff --git a/.config/webpack/webpack.config.ts b/.config/webpack/webpack.config.ts index a804537..37d895d 100644 --- a/.config/webpack/webpack.config.ts +++ b/.config/webpack/webpack.config.ts @@ -13,189 +13,201 @@ import path from 'path'; import ReplaceInFileWebpackPlugin from 'replace-in-file-webpack-plugin'; import { Configuration } from 'webpack'; -import { getPackageJson, getPluginJson, hasReadme, getEntries } from './utils'; +import { getPackageJson, getPluginJson, hasReadme, getEntries, isWSL } from './utils'; import { SOURCE_DIR, DIST_DIR } from './constants'; const pluginJson = getPluginJson(); -const config = async (env): Promise => ({ - cache: { - type: 'filesystem', - buildDependencies: { - config: [__filename], - }, - }, - - context: path.join(process.cwd(), SOURCE_DIR), - - devtool: env.production ? 'source-map' : 'eval-source-map', - - entry: await getEntries(), - - externals: [ - 'lodash', - 'jquery', - 'moment', - 'slate', - 'emotion', - '@emotion/react', - '@emotion/css', - 'prismjs', - 'slate-plain-serializer', - '@grafana/slate-react', - 'react', - 'react-dom', - 'react-redux', - 'redux', - 'rxjs', - 'react-router', - 'react-router-dom', - 'd3', - 'angular', - '@grafana/ui', - '@grafana/runtime', - '@grafana/data', - - // Mark legacy SDK imports as external if their name starts with the "grafana/" prefix - ({ request }, callback) => { - const prefix = 'grafana/'; - const hasPrefix = (request) => request.indexOf(prefix) === 0; - const stripPrefix = (request) => request.substr(prefix.length); - - if (hasPrefix(request)) { - return callback(undefined, stripPrefix(request)); - } - - callback(); +const config = async (env): Promise => { + const baseConfig: Configuration = { + cache: { + type: 'filesystem', + buildDependencies: { + config: [__filename], + }, }, - ], - - mode: env.production ? 'production' : 'development', - - module: { - rules: [ - { - exclude: /(node_modules)/, - test: /\.[tj]sx?$/, - use: { - loader: 'swc-loader', - options: { - jsc: { - baseUrl: './src', - target: 'es2015', - loose: false, - parser: { - syntax: 'typescript', - tsx: true, - decorators: false, - dynamicImport: true, + + context: path.join(process.cwd(), SOURCE_DIR), + + devtool: env.production ? 'source-map' : 'eval-source-map', + + entry: await getEntries(), + + externals: [ + 'lodash', + 'jquery', + 'moment', + 'slate', + 'emotion', + '@emotion/react', + '@emotion/css', + 'prismjs', + 'slate-plain-serializer', + '@grafana/slate-react', + 'react', + 'react-dom', + 'react-redux', + 'redux', + 'rxjs', + 'react-router', + 'react-router-dom', + 'd3', + 'angular', + '@grafana/ui', + '@grafana/runtime', + '@grafana/data', + + // Mark legacy SDK imports as external if their name starts with the "grafana/" prefix + ({ request }, callback) => { + const prefix = 'grafana/'; + const hasPrefix = (request) => request.indexOf(prefix) === 0; + const stripPrefix = (request) => request.substr(prefix.length); + + if (hasPrefix(request)) { + return callback(undefined, stripPrefix(request)); + } + + callback(); + }, + ], + + mode: env.production ? 'production' : 'development', + + module: { + rules: [ + { + exclude: /(node_modules)/, + test: /\.[tj]sx?$/, + use: { + loader: 'swc-loader', + options: { + jsc: { + baseUrl: './src', + target: 'es2015', + loose: false, + parser: { + syntax: 'typescript', + tsx: true, + decorators: false, + dynamicImport: true, + }, }, }, }, }, + { + test: /\.css$/, + use: ["style-loader", "css-loader"] + }, + { + test: /\.s[ac]ss$/, + use: ['style-loader', 'css-loader', 'sass-loader'], + }, + { + test: /\.(png|jpe?g|gif|svg)$/, + type: 'asset/resource', + generator: { + // Keep publicPath relative for host.com/grafana/ deployments + publicPath: `public/plugins/${pluginJson.id}/img/`, + outputPath: 'img/', + filename: Boolean(env.production) ? '[hash][ext]' : '[name][ext]', + }, + }, + { + test: /\.(woff|woff2|eot|ttf|otf)(\?v=\d+\.\d+\.\d+)?$/, + type: 'asset/resource', + generator: { + // Keep publicPath relative for host.com/grafana/ deployments + publicPath: `public/plugins/${pluginJson.id}/fonts/`, + outputPath: 'fonts/', + filename: Boolean(env.production) ? '[hash][ext]' : '[name][ext]', + }, + }, + ], + }, + + output: { + clean: { + keep: new RegExp(`(.*?_(amd64|arm(64)?)(.exe)?|go_plugin_build_manifest)`), }, - { - test: /\.css$/, - use: ["style-loader", "css-loader"] - }, - { - test: /\.s[ac]ss$/, - use: ['style-loader', 'css-loader', 'sass-loader'], + filename: '[name].js', + library: { + type: 'amd', }, - { - test: /\.(png|jpe?g|gif|svg)$/, - type: 'asset/resource', - generator: { - // Keep publicPath relative for host.com/grafana/ deployments - publicPath: `public/plugins/${pluginJson.id}/img/`, - outputPath: 'img/', - filename: Boolean(env.production) ? '[hash][ext]' : '[name][ext]', + path: path.resolve(process.cwd(), DIST_DIR), + publicPath: '/', + }, + + plugins: [ + new CopyWebpackPlugin({ + patterns: [ + // If src/README.md exists use it; otherwise the root README + // To `compiler.options.output` + { from: hasReadme() ? 'README.md' : '../README.md', to: '.', force: true }, + { from: 'plugin.json', to: '.' }, + { from: '../LICENSE', to: '.' }, + { from: '../CHANGELOG.md', to: '.', force: true }, + { from: '**/*.json', to: '.' }, // TODO + { from: '**/*.svg', to: '.', noErrorOnMissing: true }, // Optional + { from: '**/*.png', to: '.', noErrorOnMissing: true }, // Optional + { from: '**/*.html', to: '.', noErrorOnMissing: true }, // Optional + { from: 'img/**/*', to: '.', noErrorOnMissing: true }, // Optional + { from: 'libs/**/*', to: '.', noErrorOnMissing: true }, // Optional + { from: 'static/**/*', to: '.', noErrorOnMissing: true }, // Optional + ], + }), + // Replace certain template-variables in the README and plugin.json + new ReplaceInFileWebpackPlugin([ + { + dir: DIST_DIR, + files: ['plugin.json', 'README.md'], + rules: [ + { + search: /\%VERSION\%/g, + replace: getPackageJson().version, + }, + { + search: /\%TODAY\%/g, + replace: new Date().toISOString().substring(0, 10), + }, + { + search: /\%PLUGIN_ID\%/g, + replace: pluginJson.id, + }, + ], }, - }, - { - test: /\.(woff|woff2|eot|ttf|otf)(\?v=\d+\.\d+\.\d+)?$/, - type: 'asset/resource', - generator: { - // Keep publicPath relative for host.com/grafana/ deployments - publicPath: `public/plugins/${pluginJson.id}/fonts`, - outputPath: 'fonts/', - filename: Boolean(env.production) ? '[hash][ext]' : '[name][ext]', + ]), + new ForkTsCheckerWebpackPlugin({ + async: Boolean(env.development), + issue: { + include: [{ file: '**/*.{ts,tsx}' }], }, - }, + typescript: { configFile: path.join(process.cwd(), 'tsconfig.json') }, + }), + new ESLintPlugin({ + extensions: ['.ts', '.tsx'], + lintDirtyModulesOnly: Boolean(env.development), // don't lint on start, only lint changed files + }), + ...(env.development ? [new LiveReloadPlugin()] : []), ], - }, - output: { - clean: { - keep: new RegExp(`.*?_(amd64|arm(64)?)(.exe)?`), - }, - filename: '[name].js', - library: { - type: 'amd', + resolve: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + // handle resolving "rootDir" paths + modules: [path.resolve(process.cwd(), 'src'), 'node_modules'], + unsafeCache: true, }, - path: path.resolve(process.cwd(), DIST_DIR), - publicPath: '/', - }, - - plugins: [ - new CopyWebpackPlugin({ - patterns: [ - // If src/README.md exists use it; otherwise the root README - // To `compiler.options.output` - { from: hasReadme() ? 'README.md' : '../README.md', to: '.', force: true }, - { from: 'plugin.json', to: '.' }, - { from: '../LICENSE', to: '.' }, - { from: '../CHANGELOG.md', to: '.', force: true }, - { from: '**/*.json', to: '.' }, // TODO - { from: '**/*.svg', to: '.', noErrorOnMissing: true }, // Optional - { from: '**/*.png', to: '.', noErrorOnMissing: true }, // Optional - { from: '**/*.html', to: '.', noErrorOnMissing: true }, // Optional - { from: 'img/**/*', to: '.', noErrorOnMissing: true }, // Optional - { from: 'libs/**/*', to: '.', noErrorOnMissing: true }, // Optional - { from: 'static/**/*', to: '.', noErrorOnMissing: true }, // Optional - ], - }), - // Replace certain template-variables in the README and plugin.json - new ReplaceInFileWebpackPlugin([ - { - dir: DIST_DIR, - files: ['plugin.json', 'README.md'], - rules: [ - { - search: /\%VERSION\%/g, - replace: getPackageJson().version, - }, - { - search: /\%TODAY\%/g, - replace: new Date().toISOString().substring(0, 10), - }, - { - search: /\%PLUGIN_ID\%/g, - replace: pluginJson.id, - }, - ], - }, - ]), - new ForkTsCheckerWebpackPlugin({ - async: Boolean(env.development), - issue: { - include: [{ file: '**/*.{ts,tsx}' }], - }, - typescript: { configFile: path.join(process.cwd(), 'tsconfig.json') }, - }), - new ESLintPlugin({ - extensions: ['.ts', '.tsx'], - lintDirtyModulesOnly: Boolean(env.development), // don't lint on start, only lint changed files - }), - ...(env.development ? [new LiveReloadPlugin()] : []), - ], - - resolve: { - extensions: ['.js', '.jsx', '.ts', '.tsx'], - // handle resolving "rootDir" paths - modules: [path.resolve(process.cwd(), 'src'), 'node_modules'], - unsafeCache: true, - }, -}); + } + + if(isWSL()) { + baseConfig.watchOptions = { + poll: 3000, + ignored: /node_modules/, + }} + + + return baseConfig; + +}; export default config; diff --git a/package.json b/package.json index de9806d..f999925 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,10 @@ "scripts": { "build": "webpack -c ./.config/webpack/webpack.config.ts --env production", "dev": "webpack -w -c ./.config/webpack/webpack.config.ts --env development", - "e2e": "yarn cypress install && yarn grafana-e2e run", - "e2e:update": "yarn cypress install && yarn grafana-e2e run --update-screenshots", + "e2e": "yarn exec cypress install && yarn exec grafana-e2e run", + "e2e:update": "yarn exec cypress install && yarn exec grafana-e2e run --update-screenshots", "lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx .", - "lint:fix": "yarn lint --fix", + "lint:fix": "yarn run lint --fix", "server": "docker-compose up --build", "sign": "npx --yes @grafana/sign-plugin@latest", "test": "jest --watch --onlyChanged", @@ -29,60 +29,62 @@ }, "engineStrict": true, "devDependencies": { - "@babel/core": "^7.16.7", - "@grafana/e2e": "9.3.8", - "@grafana/e2e-selectors": "9.3.8", - "@grafana/eslint-config": "^5.1.0", + "@babel/core": "^7.21.4", + "@grafana/e2e": "10.0.3", + "@grafana/e2e-selectors": "10.0.3", + "@grafana/eslint-config": "^6.0.0", "@grafana/tsconfig": "^1.2.0-rc1", - "@swc/core": "^1.2.144", - "@swc/helpers": "^0.4.12", - "@swc/jest": "^0.2.23", - "@testing-library/jest-dom": "^5.16.2", + "@swc/core": "1.3.75", + "@swc/helpers": "^0.5.0", + "@swc/jest": "^0.2.26", + "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^12.1.4", "@types/debounce-promise": "^3.1.6", - "@types/glob": "^8.0.0", + "@types/glob": "^8.1.0", "@types/grafana": "github:CorpGlory/types-grafana", - "@types/jest": "^29.2.2", - "@types/jquery": "3.5.16", - "@types/lodash": "^4.14.188", + "@types/jest": "^29.5.0", + "@types/jquery": "3.5.17", + "@types/lodash": "^4.14.194", "@types/mathjs": "^9.4.1", "@types/ms": "^0.7.31", - "@types/node": "^18.11.9", + "@types/node": "^18.15.11", "@types/react-test-renderer": "18.0.0", - "@types/uuid": "^9.0.1", + "@types/uuid": "^9.0.2", "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.7.1", - "eslint-webpack-plugin": "^3.1.1", - "fork-ts-checker-webpack-plugin": "^7.2.0", - "glob": "^8.0.3", + "css-loader": "^6.7.3", + "eslint-webpack-plugin": "^4.0.1", + "fork-ts-checker-webpack-plugin": "^8.0.0", + "glob": "^10.2.7", "identity-obj-proxy": "3.0.0", - "jest": "^29.3.1", - "jest-environment-jsdom": "^29.3.1", - "prettier": "^2.5.0", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "prettier": "^2.8.7", "react-test-renderer": "18.2.0", "replace-in-file-webpack-plugin": "^1.0.6", - "sass": "1.56.1", - "sass-loader": "13.2.0", - "style-loader": "3.3.1", + "sass": "1.63.2", + "sass-loader": "13.3.1", + "style-loader": "3.3.3", "swc-loader": "^0.2.3", - "ts-node": "^10.5.0", - "tsconfig-paths": "^4.1.0", - "typescript": "^4.4.0", - "webpack": "^5.69.1", - "webpack-cli": "^4.9.2", + "ts-node": "^10.9.1", + "tsconfig-paths": "^4.2.0", + "typescript": "4.8.4", + "webpack": "^5.86.0", + "webpack-cli": "^5.1.4", "webpack-livereload-plugin": "^3.0.2" }, "homepage": "https://github.com/sasaki77/archiverappliance-datasource#readme", "dependencies": { "@emotion/css": "^11.1.3", - "@grafana/data": "9.4.7", - "@grafana/runtime": "9.4.7", - "@grafana/ui": "9.4.7", + "@grafana/data": "10.0.3", + "@grafana/runtime": "10.0.3", + "@grafana/ui": "10.0.3", "debounce-promise": "^3.1.2", - "mathjs": "^11.7.0", + "mathjs": "^11.9.1", "ms": "^2.1.3", "react": "17.0.2", "react-dom": "17.0.2", - "uuid": "^9.0.0" - } -} \ No newline at end of file + "uuid": "^9.0.0", + "tslib": "2.5.3" + }, + "packageManager": "yarn@1.22.19" +} diff --git a/src/components/__snapshots__/ConfigEditor.test.tsx.snap b/src/components/__snapshots__/ConfigEditor.test.tsx.snap index 5940eb3..ea33993 100644 --- a/src/components/__snapshots__/ConfigEditor.test.tsx.snap +++ b/src/components/__snapshots__/ConfigEditor.test.tsx.snap @@ -81,7 +81,7 @@ exports[`Render should render component 1`] = ` class="css-1w5c5dq-input-inputWrapper" >