From 859c12bbb4d6bde63a11dd5b7f53d1bd82bd2b38 Mon Sep 17 00:00:00 2001 From: Jack Westbrook Date: Tue, 4 Mar 2025 09:55:41 +0100 Subject: [PATCH] Grafana UI: Expose unstable entrypoint (#97080) * feat(grafana-ui): build unstable entrypoint for experimental components * feat(plugins): expose grafana/ui/unstable * build(grafana-ui): add rollup plugin to create alias package.json for unstable entrypoint * build(packages): rewrite prepare npm script to generate alias packagejson files * chore(packages): use relative paths in publishConfig for exports generation * chore(frontend): move npmcli/package-json package to root package.json * revert(grafana-ui): remove rollup plugin for generating alias package.json files * chore(grafana-ui): clean up unstable directory postpack to prevent yarn lock issues * build(packages): fix scope for pkgName usage * feat(packages): create separate cjs and esm builds that validate with arethetypeswrong cli * chore(yarn): refresh lock file * fix(packages): make sure alias package.jsons point to existing files --- .github/CODEOWNERS | 2 +- .gitignore | 1 + package.json | 1 + packages/grafana-data/package.json | 8 +- packages/grafana-e2e-selectors/package.json | 10 +-- packages/grafana-flamegraph/package.json | 8 +- packages/grafana-icons/package.json | 6 +- packages/grafana-prometheus/package.json | 8 +- packages/grafana-runtime/package.json | 8 +- packages/grafana-schema/package.json | 8 +- packages/grafana-ui/package.json | 12 +-- packages/grafana-ui/rollup.config.ts | 25 ++++++ packages/rollup.config.parts.ts | 18 ++-- .../plugins/loader/sharedDependencies.ts | 1 + scripts/prepare-npm-package.js | 84 +++++++++++++++++++ scripts/prepare-packagejson.js | 21 ----- scripts/validate-npm-packages.sh | 14 ++-- yarn.lock | 27 +++++- 18 files changed, 193 insertions(+), 69 deletions(-) create mode 100644 scripts/prepare-npm-package.js delete mode 100644 scripts/prepare-packagejson.js diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7f6fd0c4f91..d2fe4b77837 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -620,7 +620,7 @@ playwright.config.ts @grafana/plugins-platform-frontend /scripts/import_many_dashboards.sh @torkelo /scripts/mixin-check.sh @bergquist /scripts/openapi3/ @grafana/grafana-operator-experience-squad -/scripts/prepare-packagejson.js @grafana/frontend-ops +/scripts/prepare-npm-package.js @grafana/frontend-ops /scripts/protobuf-check.sh @grafana/plugins-platform-backend /scripts/stripnulls.sh @grafana/grafana-as-code /scripts/tag_release.sh @grafana/grafana-developer-enablement-squad diff --git a/.gitignore b/.gitignore index c043094a016..1a8be100668 100644 --- a/.gitignore +++ b/.gitignore @@ -148,6 +148,7 @@ pkg/services/quota/quotaimpl/storage/storage.json # Ignoring frontend packages specifics /packages/grafana-ui/.yarn/.cache /packages/grafana-ui/.storybook/static +/packages/grafana-ui/unstable /packages/**/dist /packages/**/compiled /packages/**/.rpt2_cache diff --git a/package.json b/package.json index 1660de6b891..1c9393c4bd9 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "@grafana/plugin-e2e": "1.17.1", "@grafana/tsconfig": "^2.0.0", "@manypkg/get-packages": "^2.2.0", + "@npmcli/package-json": "^5.2.0", "@playwright/test": "1.50.1", "@pmmmwh/react-refresh-webpack-plugin": "0.5.15", "@react-types/button": "3.10.2", diff --git a/packages/grafana-data/package.json b/packages/grafana-data/package.json index 61124640e72..6ae5450bf1f 100644 --- a/packages/grafana-data/package.json +++ b/packages/grafana-data/package.json @@ -16,9 +16,9 @@ "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { - "main": "dist/index.js", - "module": "dist/esm/index.js", - "types": "dist/index.d.ts", + "main": "./dist/cjs/index.cjs", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.cts", "access": "public" }, "files": [ @@ -31,7 +31,7 @@ "build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild", "clean": "rimraf ./dist ./compiled ./package.tgz", "typecheck": "tsc --emitDeclarationOnly false --noEmit", - "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js", + "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", "postpack": "mv package.json.bak package.json" }, "dependencies": { diff --git a/packages/grafana-e2e-selectors/package.json b/packages/grafana-e2e-selectors/package.json index 739e36cfec8..dc8a0541bcf 100644 --- a/packages/grafana-e2e-selectors/package.json +++ b/packages/grafana-e2e-selectors/package.json @@ -19,9 +19,9 @@ "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { - "main": "dist/index.js", - "module": "dist/esm/index.js", - "types": "dist/index.d.ts", + "main": "./dist/cjs/index.cjs", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.cts", "access": "public" }, "files": [ @@ -35,7 +35,7 @@ "bundle": "rollup -c rollup.config.ts --configPlugin esbuild", "clean": "rimraf ./dist ./compiled ./package.tgz", "typecheck": "tsc --emitDeclarationOnly false --noEmit", - "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js", + "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", "postpack": "mv package.json.bak package.json" }, "devDependencies": { @@ -51,7 +51,7 @@ }, "dependencies": { "@grafana/tsconfig": "^2.0.0", - "semver": "7.7.0", + "semver": "^7.7.0", "tslib": "2.8.1", "typescript": "5.7.3" } diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index f47339c0aa0..9ce7c7e25c5 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -19,9 +19,9 @@ "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { - "main": "dist/index.js", - "module": "dist/esm/index.js", - "types": "dist/index.d.ts", + "main": "./dist/cjs/index.cjs", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.cts", "access": "public" }, "files": [ @@ -35,7 +35,7 @@ "bundle": "rollup -c rollup.config.ts --configPlugin esbuild", "clean": "rimraf ./dist ./compiled ./package.tgz", "typecheck": "tsc --emitDeclarationOnly false --noEmit", - "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js", + "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", "postpack": "mv package.json.bak package.json" }, "browserslist": [ diff --git a/packages/grafana-icons/package.json b/packages/grafana-icons/package.json index cb60d576dd5..08bb3641efb 100644 --- a/packages/grafana-icons/package.json +++ b/packages/grafana-icons/package.json @@ -15,9 +15,9 @@ "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.mts", "access": "public" }, "files": [ diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index 9cf1a0c1100..59690be5c05 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -22,9 +22,9 @@ "./LICENSE_AGPL" ], "publishConfig": { - "main": "dist/index.js", - "module": "dist/esm/index.js", - "types": "dist/index.d.ts", + "main": "./dist/cjs/index.cjs", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.cts", "access": "public" }, "scripts": { @@ -32,7 +32,7 @@ "bundle": "rollup -c rollup.config.ts --configPlugin esbuild", "clean": "rimraf ./dist ./compiled ./package.tgz", "typecheck": "tsc --emitDeclarationOnly false --noEmit", - "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js", + "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", "postpack": "mv package.json.bak package.json" }, "dependencies": { diff --git a/packages/grafana-runtime/package.json b/packages/grafana-runtime/package.json index 90ca6774646..058e41e4d49 100644 --- a/packages/grafana-runtime/package.json +++ b/packages/grafana-runtime/package.json @@ -17,9 +17,9 @@ "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { - "main": "dist/index.js", - "module": "dist/esm/index.js", - "types": "dist/index.d.ts", + "main": "./dist/cjs/index.cjs", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.cts", "access": "public" }, "files": [ @@ -33,7 +33,7 @@ "bundle": "rollup -c rollup.config.ts --configPlugin esbuild", "clean": "rimraf ./dist ./compiled ./package.tgz", "typecheck": "tsc --emitDeclarationOnly false --noEmit", - "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js", + "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", "postpack": "mv package.json.bak package.json" }, "dependencies": { diff --git a/packages/grafana-schema/package.json b/packages/grafana-schema/package.json index a4939ade8a1..3a97767a218 100644 --- a/packages/grafana-schema/package.json +++ b/packages/grafana-schema/package.json @@ -16,9 +16,9 @@ "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { - "main": "dist/index.js", - "module": "dist/esm/index.js", - "types": "dist/index.d.ts", + "main": "./dist/cjs/index.cjs", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.cts", "access": "public" }, "files": [ @@ -32,7 +32,7 @@ "bundle": "rollup -c rollup.config.ts --configPlugin esbuild", "clean": "rimraf ./dist ./compiled ./package.tgz", "typecheck": "tsc --emitDeclarationOnly false --noEmit", - "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js", + "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-npm-package.js", "postpack": "mv package.json.bak package.json" }, "devDependencies": { diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 1116e1b2980..4b51ff9386a 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -19,9 +19,9 @@ "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { - "main": "dist/index.js", - "module": "dist/esm/index.js", - "types": "dist/index.d.ts", + "main": "./dist/cjs/index.cjs", + "module": "./dist/esm/index.mjs", + "types": "./dist/cjs/index.d.cts", "access": "public" }, "files": [ @@ -34,12 +34,12 @@ "scripts": { "build": "tsc -p ./tsconfig.build.json && rollup -c rollup.config.ts --configPlugin esbuild", "bundle": "rollup -c rollup.config.ts --configPlugin esbuild", - "clean": "rimraf ./dist ./compiled ./package.tgz", + "clean": "rimraf ./dist ./compiled ./unstable ./package.tgz", "storybook": "storybook dev -p 9001 -c .storybook --no-open", "storybook:build": "storybook build -o ./dist/storybook -c .storybook", "typecheck": "tsc --emitDeclarationOnly false --noEmit", - "prepack": "cp package.json package.json.bak && node ../../scripts/prepare-packagejson.js", - "postpack": "mv package.json.bak package.json" + "prepack": "cp package.json package.json.bak && ALIAS_PACKAGE_NAME=unstable node ../../scripts/prepare-npm-package.js", + "postpack": "mv package.json.bak package.json && rimraf ./unstable" }, "browserslist": [ "defaults", diff --git a/packages/grafana-ui/rollup.config.ts b/packages/grafana-ui/rollup.config.ts index d3cb5c2d8b8..5dcad62f82f 100644 --- a/packages/grafana-ui/rollup.config.ts +++ b/packages/grafana-ui/rollup.config.ts @@ -25,5 +25,30 @@ export default [ ], output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-ui')], }, + { + input: 'src/unstable.ts', + plugins: [ + ...plugins, + svg({ stringify: true }), + copy({ + targets: [{ src: iconSrcPaths, dest: './dist/public/' }], + flatten: false, + }), + ], + output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-ui')], + }, tsDeclarationOutput(pkg), + tsDeclarationOutput(pkg, { + input: './compiled/unstable.d.ts', + output: [ + { + file: './dist/cjs/unstable.d.cts', + format: 'cjs', + }, + { + file: './dist/esm/unstable.d.mts', + format: 'es', + }, + ], + }), ]; diff --git a/packages/rollup.config.parts.ts b/packages/rollup.config.parts.ts index ca805b07ed9..41b760a3acf 100644 --- a/packages/rollup.config.parts.ts +++ b/packages/rollup.config.parts.ts @@ -1,6 +1,6 @@ // This file contains the common parts of the rollup configuration that are shared across multiple packages. import nodeResolve from '@rollup/plugin-node-resolve'; -import { dirname, resolve } from 'node:path'; +import { dirname, join, resolve } from 'node:path'; import dts from 'rollup-plugin-dts'; import esbuild from 'rollup-plugin-esbuild'; import { nodeExternals } from 'rollup-plugin-node-externals'; @@ -28,6 +28,7 @@ export function cjsOutput(pkg) { format: 'cjs', sourcemap: true, dir: dirname(pkg.publishConfig.main), + entryFileNames: '[name].cjs', esModule: true, interop: 'compat', }; @@ -39,6 +40,7 @@ export function esmOutput(pkg, pkgName) { format: 'esm', sourcemap: true, dir: dirname(pkg.publishConfig.module), + entryFileNames: '[name].mjs', preserveModules: true, preserveModulesRoot: resolve(projectCwd, `packages/${pkgName}/src`), }; @@ -49,10 +51,16 @@ export function tsDeclarationOutput(pkg, overrides = {}) { return { input: './compiled/index.d.ts', plugins: [dts()], - output: { - file: pkg.publishConfig.types, - format: 'es', - }, + output: [ + { + file: pkg.publishConfig.types, + format: 'cjs', + }, + { + file: join(dirname(pkg.publishConfig.module), 'index.d.mts'), + format: 'es', + }, + ], ...overrides, }; } diff --git a/public/app/features/plugins/loader/sharedDependencies.ts b/public/app/features/plugins/loader/sharedDependencies.ts index 7059274c717..c294491c6ac 100644 --- a/public/app/features/plugins/loader/sharedDependencies.ts +++ b/public/app/features/plugins/loader/sharedDependencies.ts @@ -52,6 +52,7 @@ export const sharedDependenciesMap = { '@grafana/runtime': grafanaRuntime, '@grafana/slate-react': () => import('slate-react'), '@grafana/ui': grafanaUI, + '@grafana/ui/unstable': () => import('@grafana/ui/src/unstable'), '@kusto/monaco-kusto': () => import('@kusto/monaco-kusto'), 'app/core/app_events': { default: appEvents, diff --git a/scripts/prepare-npm-package.js b/scripts/prepare-npm-package.js new file mode 100644 index 00000000000..07559074842 --- /dev/null +++ b/scripts/prepare-npm-package.js @@ -0,0 +1,84 @@ +import PackageJson from '@npmcli/package-json'; +import { mkdir } from 'node:fs/promises'; +import { join, dirname } from 'node:path'; + +const cwd = process.cwd(); + +try { + const pkgJson = await PackageJson.load(cwd); + const cjsIndex = pkgJson.content.publishConfig?.main ?? pkgJson.content.main; + const esmIndex = pkgJson.content.publishConfig?.module ?? pkgJson.content.module; + const cjsTypes = pkgJson.content.publishConfig?.types ?? pkgJson.content.types; + const esmTypes = `./${join(dirname(esmIndex), 'index.d.mts')}`; + + const exports = { + './package.json': './package.json', + '.': { + import: { + types: esmTypes, + default: esmIndex, + }, + require: { + types: cjsTypes, + default: cjsIndex, + }, + }, + }; + + pkgJson.update({ + main: cjsIndex, + types: cjsTypes, + module: esmIndex, + exports, + }); + + await pkgJson.save(); + + // If an alias package name is provided we add an exports entry for the alias + // then generate an additional "nested" package.json for typescript resolution that + // doesn't use the exports property in package.json. + if (process.env.ALIAS_PACKAGE_NAME) { + const aliasName = process.env.ALIAS_PACKAGE_NAME; + pkgJson.update({ + exports: { + ...pkgJson.content.exports, + [`./${aliasName}`]: { + import: { + types: esmTypes.replace('index', aliasName), + default: esmIndex.replace('index', aliasName), + }, + require: { + types: cjsTypes.replace('index', aliasName), + default: cjsTypes.replace('index', aliasName), + }, + }, + }, + files: [...pkgJson.content.files, aliasName], + }); + await pkgJson.save(); + await createAliasPackageJsonFiles(pkgJson.content, aliasName); + } +} catch (e) { + console.error(e); + process.exit(1); +} + +async function createAliasPackageJsonFiles(packageJsonContent, aliasName) { + const pkgName = `${packageJsonContent.name}/${aliasName}`; + try { + console.log(`📦 Writing alias package.json for ${pkgName}.`); + const pkgJsonPath = `${cwd}/${aliasName}`; + await mkdir(pkgJsonPath, { recursive: true }); + const pkgJson = await PackageJson.create(pkgJsonPath, { + data: { + name: pkgName, + types: `../dist/cjs/${aliasName}.d.cts`, + main: `../dist/cjs/${aliasName}.cjs`, + module: `../dist/esm/${aliasName}.mjs`, + }, + }); + await pkgJson.save(); + } catch (error) { + throw new Error(`Error generating package.json for ${pkgName}`, error); + } +} diff --git a/scripts/prepare-packagejson.js b/scripts/prepare-packagejson.js deleted file mode 100644 index cab9eed4c98..00000000000 --- a/scripts/prepare-packagejson.js +++ /dev/null @@ -1,21 +0,0 @@ -const fs = require('fs'); - -const cwd = process.cwd(); -const packageJson = require(`${cwd}/package.json`); - -const newPackageJson = { - ...packageJson, - main: packageJson.publishConfig?.main ?? packageJson.main, -}; - -if (packageJson.publishConfig?.types) { - newPackageJson.types = packageJson.publishConfig.types; -} - -if (packageJson.publishConfig?.module) { - newPackageJson.module = packageJson.publishConfig.module; -} - -try { - fs.writeFileSync(`${cwd}/package.json`, JSON.stringify(newPackageJson, null, 2)); -} catch (e) {} diff --git a/scripts/validate-npm-packages.sh b/scripts/validate-npm-packages.sh index 838dccd91e1..2f0f1764597 100755 --- a/scripts/validate-npm-packages.sh +++ b/scripts/validate-npm-packages.sh @@ -43,14 +43,14 @@ for file in "$ARTIFACTS_DIR"/*.tgz; do fi # Assert commonjs builds - if [ ! -d dist ] || [ ! -f dist/index.js ] || [ ! -f dist/index.d.ts ]; then + if [ ! -d dist ] || [ ! -f dist/cjs/index.cjs ] || [ ! -f dist/cjs/index.d.cts ]; then echo -e "❌ Failed: Missing 'dist' directory or required commonjs files in package $dir_name.\n" exit 1 fi - if [ "$(jq -r '.main' package.json)" != "dist/index.js" ] || \ - [ "$(jq -r '.types' package.json)" != "dist/index.d.ts" ]; then - echo -e "❌ Failed: Incorrect package.json properties in package $dir_name.\n" + if [ "$(jq -r '.main' package.json)" != "./dist/cjs/index.cjs" ] || \ + [ "$(jq -r '.types' package.json)" != "./dist/cjs/index.d.cts" ]; then + echo -e "❌ Failed: Incorrect cjs package.json properties in package $dir_name.\n" exit 1 fi @@ -58,13 +58,13 @@ for file in "$ARTIFACTS_DIR"/*.tgz; do esm_packages=("grafana-data" "grafana-ui" "grafana-runtime" "grafana-e2e-selectors" "grafana-schema") for esm_package in "${esm_packages[@]}"; do if [[ "$dir_name" == "$esm_package" ]]; then - if [ ! -d dist/esm ] || [ ! -f dist/esm/index.js ]; then + if [ ! -d dist/esm ] || [ ! -f dist/esm/index.mjs ]; then echo -e "❌ Failed: Missing 'dist/esm' directory or required esm files in package $dir_name.\n" exit 1 fi - if [ "$(jq -r '.module' package.json)" != "dist/esm/index.js" ]; then - echo -e "❌ Failed: Incorrect package.json properties in package $dir_name.\n" + if [ "$(jq -r '.module' package.json)" != "./dist/esm/index.mjs" ]; then + echo -e "❌ Failed: Incorrect esm package.json properties in package $dir_name.\n" exit 1 fi fi diff --git a/yarn.lock b/yarn.lock index cb307fe09ce..dce63788f1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3110,7 +3110,7 @@ __metadata: rollup-plugin-dts: "npm:^6.1.1" rollup-plugin-esbuild: "npm:6.2.0" rollup-plugin-node-externals: "npm:^8.0.0" - semver: "npm:7.7.0" + semver: "npm:^7.7.0" tslib: "npm:2.8.1" typescript: "npm:5.7.3" languageName: unknown @@ -5444,6 +5444,21 @@ __metadata: languageName: node linkType: hard +"@npmcli/package-json@npm:^5.2.0": + version: 5.2.1 + resolution: "@npmcli/package-json@npm:5.2.1" + dependencies: + "@npmcli/git": "npm:^5.0.0" + glob: "npm:^10.2.2" + hosted-git-info: "npm:^7.0.0" + json-parse-even-better-errors: "npm:^3.0.0" + normalize-package-data: "npm:^6.0.0" + proc-log: "npm:^4.0.0" + semver: "npm:^7.5.3" + checksum: 10/304a819b93f79a6e0e56cb371961a66d2db72142e310d545ecbbbe4d917025a30601aa8e63a5f0cc28f0fe281c116bdaf79b334619b105a1d027a2b769ecd137 + languageName: node + linkType: hard + "@npmcli/promise-spawn@npm:^7.0.0": version: 7.0.2 resolution: "@npmcli/promise-spawn@npm:7.0.2" @@ -18143,6 +18158,7 @@ __metadata: "@manypkg/get-packages": "npm:^2.2.0" "@msagl/core": "npm:^1.1.19" "@msagl/parser": "npm:^1.1.19" + "@npmcli/package-json": "npm:^5.2.0" "@opentelemetry/api": "npm:1.9.0" "@opentelemetry/exporter-collector": "npm:0.25.0" "@opentelemetry/semantic-conventions": "npm:1.28.0" @@ -28361,6 +28377,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.7.0": + version: 7.7.1 + resolution: "semver@npm:7.7.1" + bin: + semver: bin/semver.js + checksum: 10/4cfa1eb91ef3751e20fc52e47a935a0118d56d6f15a837ab814da0c150778ba2ca4f1a4d9068b33070ea4273629e615066664c2cfcd7c272caf7a8a0f6518b2c + languageName: node + linkType: hard + "send@npm:0.19.0": version: 0.19.0 resolution: "send@npm:0.19.0"