Skip to content

Commit

Permalink
Merge pull request #10130 from vojtechszocs/rework-sdk-npm-pkg
Browse files Browse the repository at this point in the history
Bug 2002362: Rework dynamic plugin SDK dist packages
  • Loading branch information
openshift-merge-robot authored Sep 24, 2021
2 parents 281244e + fb1f632 commit 45811f4
Show file tree
Hide file tree
Showing 26 changed files with 650 additions and 103 deletions.
4 changes: 2 additions & 2 deletions frontend/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
__coverage__
**/node_modules
**/dist
gui_test_screenshots
**/generated
*.min.js
gui_test_screenshots
public/lib
Godeps
@types
dynamic-demo-plugin
packages/console-dynamic-plugin-sdk/schema
5 changes: 2 additions & 3 deletions frontend/dynamic-demo-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
"i18n": "i18next \"src/**/*.{js,jsx,ts,tsx}\" [-oc] -c i18next-parser.config.js",
"ts-node": "ts-node -O '{\"module\":\"commonjs\"}' -I '/node_modules/(?!(@openshift-console)/)/'"
},
"dependencies": {
"@openshift-console/dynamic-plugin-sdk": "file:../packages/console-dynamic-plugin-sdk/dist"
},
"devDependencies": {
"@openshift-console/dynamic-plugin-sdk": "file:../packages/console-dynamic-plugin-sdk/dist/core",
"@openshift-console/dynamic-plugin-sdk-webpack": "file:../packages/console-dynamic-plugin-sdk/dist/webpack",
"@types/react": "16.8.13",
"copy-webpack-plugin": "^6.4.1",
"http-server": "0.12.x",
Expand Down
2 changes: 1 addition & 1 deletion frontend/dynamic-demo-plugin/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import * as webpack from 'webpack';
import * as path from 'path';
import { ConsoleRemotePlugin } from '@openshift-console/dynamic-plugin-sdk/lib/index-webpack';
import { ConsoleRemotePlugin } from '@openshift-console/dynamic-plugin-sdk-webpack';

const CopyWebpackPlugin = require('copy-webpack-plugin');

Expand Down
369 changes: 362 additions & 7 deletions frontend/dynamic-demo-plugin/yarn.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion frontend/packages/console-dynamic-plugin-sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
/schema
/generated
32 changes: 26 additions & 6 deletions frontend/packages/console-dynamic-plugin-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,21 @@ dynamic-demo-plugin/
└── webpack.config.ts
```

## SDK packages

| Package Name | Description |
| --- | --- |
| `@openshift-console/dynamic-plugin-sdk` | Provides the plugin API, types and utilities used by dynamic plugins at runtime. |
| `@openshift-console/dynamic-plugin-sdk-webpack` | Provides webpack plugin `ConsoleRemotePlugin` used to build all dynamic plugin assets. |
| `@openshift-console/dynamic-plugin-sdk-internal` | Internal package exposing additional code. |

## `package.json`

Plugin metadata is declared via the `consolePlugin` object.

```jsonc
{
"name": "@console/dynamic-demo-plugin",
"name": "dynamic-demo-plugin",
"version": "0.0.0",
"private": true,
// scripts, dependencies, devDependencies, ...
Expand Down Expand Up @@ -59,8 +67,7 @@ Dynamic plugins can expose modules representing additional code to be referenced
at runtime. A separate [webpack chunk](https://webpack.js.org/guides/code-splitting/) is generated for
each exposed module. Exposed modules are resolved relative to plugin's webpack `context` option.

See [`ConsolePluginMetadata` type](/frontend/packages/console-dynamic-plugin-sdk/src/schema/plugin-package.ts)
for details on the `consolePlugin` object and its schema.
See `ConsolePluginMetadata` type for details on the `consolePlugin` object and its schema.

## `console-extensions.json`

Expand Down Expand Up @@ -105,10 +112,9 @@ support for module federation.
All dynamic plugin assets are managed via webpack plugin `ConsoleRemotePlugin`.

```ts
import * as webpack from 'webpack';
import { ConsoleRemotePlugin } from '@console/dynamic-plugin-sdk/src/webpack/ConsoleRemotePlugin';
const { ConsoleRemotePlugin } = require('@openshift-console/dynamic-plugin-sdk-webpack');

const config: webpack.Configuration = {
const config = {
// 'entry' is optional, but unrelated to plugin assets
plugins: [new ConsoleRemotePlugin()],
// ... rest of webpack configuration
Expand Down Expand Up @@ -180,3 +186,17 @@ list of plugin names (disable specific plugins) or an empty string (disable all
be enabled or disabled separately.
- Failure to resolve a code reference (unable to load module, missing module export etc.) will disable
the plugin.

## Publishing SDK packages

To see the latest published version:

```sh
yarn info @openshift-console/dynamic-plugin-sdk dist-tags
```

To build and publish all distributable [SDK packages](#sdk-packages) to [npm registry](https://www.npmjs.com/):

```sh
PKG_VERSION='<new-version>' ./publish.sh
```
16 changes: 8 additions & 8 deletions frontend/packages/console-dynamic-plugin-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
"private": true,
"main": "src/index.ts",
"scripts": {
"clean": "rm -rf ./dist",
"build": "yarn clean && yarn validate && yarn generate && yarn tsc",
"clean": "rm -rf dist generated",
"build": "yarn clean && yarn validate && yarn compile && yarn generate",
"compile": "for ext in '' '-internal' '-webpack' ; do yarn tsc -p tsconfig${ext}.json ; done",
"generate": "yarn generate-schema && yarn generate-doc && yarn generate-pkg-assets",
"generate-schema": "yarn ts-node ./scripts/generate-schema.ts",
"generate-doc": "yarn ts-node ./scripts/generate-doc.ts",
"generate-pkg-assets": "yarn ts-node ./scripts/generate-pkg-assets.ts",
"validate": "yarn ts-node ./scripts/validate-extensions.ts",
"ts-node": "ts-node -O '{\"module\":\"commonjs\"}'",
"publish": "yarn build && yarn publish dist --no-git-tag-version"
"generate-schema": "yarn ts-node scripts/generate-schema.ts",
"generate-doc": "yarn ts-node scripts/generate-doc.ts",
"generate-pkg-assets": "yarn ts-node scripts/generate-pkg-assets.ts",
"validate": "yarn ts-node scripts/validate-extensions.ts",
"ts-node": "ts-node -O '{\"module\":\"commonjs\"}'"
},
"devDependencies": {
"@types/ejs": "3.x",
Expand Down
10 changes: 10 additions & 0 deletions frontend/packages/console-dynamic-plugin-sdk/publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

set -exuo pipefail

yarn build

for pkgDir in 'dist/core' 'dist/internal' 'dist/webpack'
do
yarn publish "$pkgDir" --no-git-tag-version --new-version "$PKG_VERSION"
done
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const renderTemplate = (srcFile: string, data: {}) => {
root: resolvePath('.'),
});

const outPath = resolvePath(`dist/doc/${path.parse(srcFile).name}`);
const outPath = resolvePath(`generated/doc/${path.parse(srcFile).name}`);

fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(outPath, content);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,42 @@ import chalk from 'chalk';
import * as fs from 'fs-extra';
import * as _ from 'lodash';
import * as readPkg from 'read-pkg';
import { getCorePackage, getInternalPackage, getWebpackPackage } from './package-definitions';
import { resolvePath, relativePath } from './utils/path';

const createPackageJson = (packagePath: string) => {
const packageJson = readPkg.sync({ normalize: false });
packageJson.name = '@openshift-console/dynamic-plugin-sdk';
delete packageJson.private;
packageJson.license = 'Apache-2.0';
packageJson.main = 'lib/index-lib.js';
packageJson.readme = 'README.md';
packageJson.peerDependencies = _.pick(packageJson.devDependencies, 'webpack');
delete packageJson.dependencies;
delete packageJson.devDependencies;
delete packageJson.scripts;
fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
const writePackageManifest = (manifest: readPkg.PackageJson, outDir: string) => {
const outPath = resolvePath(`${outDir}/package.json`);
fs.writeFileSync(outPath, JSON.stringify(manifest, null, 2));
console.log(chalk.green(relativePath(outPath)));
};

const preparePkgAssets = () => {
fs.mkdirSync(resolvePath('dist'), { recursive: true });
const pkgOutPath = resolvePath('dist/package.json');
console.log('Generating Console plugin package.json');
createPackageJson(pkgOutPath);
console.log(chalk.green(relativePath(pkgOutPath)));
console.log('Copying schema, license, and readme files');
fs.copySync(resolvePath('../../../LICENSE'), resolvePath('dist/LICENSE'));
fs.copySync(resolvePath('README.md'), resolvePath('dist/README.md'));
fs.copySync(resolvePath('schema'), resolvePath('dist/schema'), { recursive: true });
const copyFiles = (files: Record<any, string>) => {
Object.entries(files).forEach(([src, dest]) => {
fs.copySync(resolvePath(src), resolvePath(dest), { recursive: true });
console.log(chalk.green(relativePath(dest)));
});
};

preparePkgAssets();
const sdkPackage = readPkg.sync({ normalize: false });
const rootPackage = readPkg.sync({ cwd: resolvePath('../..'), normalize: false });

const missingDepNames = new Set<string>();
const missingDepCallback = (name: string) => missingDepNames.add(name);

const outPackages = [
getCorePackage(sdkPackage, rootPackage, missingDepCallback),
getInternalPackage(sdkPackage, rootPackage, missingDepCallback),
getWebpackPackage(sdkPackage, rootPackage, missingDepCallback),
];

if (missingDepNames.size > 0) {
console.error(`Failed to parse package dependencies: ${Array.from(missingDepNames).join(', ')}`);
process.exit(1);
}

outPackages.forEach((pkg) => {
console.log(`Generating assets for package ${chalk.bold(pkg.manifest.name)}`);

writePackageManifest(pkg.manifest, pkg.outDir);
copyFiles(_.mapValues(pkg.filesToCopy, (dest) => `${pkg.outDir}/${dest}`));
});
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ console.log('Generating Console plugin JSON schemas');
typeConfigs.forEach((tc) => {
const schema = generateSchema(tc);
const schemaString = JSON.stringify(schema, null, 2);
const outPath = resolvePath(`schema/${path.parse(tc.srcFile).name}`);
const outPath = resolvePath(`generated/schema/${path.parse(tc.srcFile).name}`);

fs.mkdirSync(path.dirname(outPath), { recursive: true });
fs.writeFileSync(`${outPath}.json`, schemaString);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import * as _ from 'lodash';
import * as readPkg from 'read-pkg';
import { getSharedPluginModules } from '../src/shared-modules';

type GeneratedPackage = {
/** Package output directory. */
outDir: string;
/** Package manifest. Note: `version` is updated via the publish script. */
manifest: readPkg.PackageJson;
/** Additional files to copy to the package output directory. */
filesToCopy: Record<any, string>;
};

type MissingDependencyCallback = (name: string) => void;

type GetPackageDefinition = (
sdkPackage: readPkg.PackageJson,
rootPackage: readPkg.PackageJson,
missingDepCallback: MissingDependencyCallback,
) => GeneratedPackage;

const commonManifestFields: Partial<readPkg.PackageJson> = {
license: 'Apache-2.0',
homepage:
'https://github.com/openshift/console/tree/master/frontend/packages/console-dynamic-plugin-sdk',
};

const commonFiles: GeneratedPackage['filesToCopy'] = {
'../../../LICENSE': 'LICENSE',
};

const parseDeps = (
pkg: readPkg.PackageJson,
depNames: string[],
missingDepCallback: MissingDependencyCallback,
) => {
const srcDeps = { ...pkg.devDependencies, ...pkg.dependencies };
depNames.filter((name) => !srcDeps[name]).forEach(missingDepCallback);
return _.pick(srcDeps, depNames);
};

const parseDepsAs = (
pkg: readPkg.PackageJson,
deps: { [depName: string]: string },
missingDepCallback: MissingDependencyCallback,
) => _.mapKeys(parseDeps(pkg, Object.keys(deps), missingDepCallback), (value, key) => deps[key]);

export const getCorePackage: GetPackageDefinition = (
sdkPackage,
rootPackage,
missingDepCallback,
) => ({
outDir: 'dist/core',
manifest: {
name: '@openshift-console/dynamic-plugin-sdk',
version: sdkPackage.version,
type: 'module',
main: 'lib/lib-core.js',
...commonManifestFields,
dependencies: parseDeps(rootPackage, getSharedPluginModules(false), missingDepCallback),
},
filesToCopy: {
...commonFiles,
'README.md': 'README.md',
'generated/doc': 'doc',
},
});

export const getInternalPackage: GetPackageDefinition = (
sdkPackage,
rootPackage,
missingDepCallback,
) => ({
outDir: 'dist/internal',
manifest: {
name: '@openshift-console/dynamic-plugin-sdk-internal',
version: sdkPackage.version,
type: 'module',
main: 'lib/lib-internal.js',
...commonManifestFields,
dependencies: getCorePackage(sdkPackage, rootPackage, missingDepCallback).manifest.dependencies,
},
filesToCopy: {
...commonFiles,
},
});

export const getWebpackPackage: GetPackageDefinition = (
sdkPackage,
rootPackage,
missingDepCallback,
) => ({
outDir: 'dist/webpack',
manifest: {
name: '@openshift-console/dynamic-plugin-sdk-webpack',
version: sdkPackage.version,
type: 'commonjs',
main: 'lib/lib-webpack.js',
...commonManifestFields,
dependencies: {
...parseDeps(sdkPackage, ['webpack'], missingDepCallback),
...parseDeps(
rootPackage,
['ajv', 'chalk', 'comment-json', 'find-up', 'read-pkg', 'semver'],
missingDepCallback,
),
...parseDepsAs(rootPackage, { 'lodash-es': 'lodash' }, missingDepCallback),
},
},
filesToCopy: {
...commonFiles,
'generated/schema': 'schema',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './api/internal-api';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { PluginStore } from '@console/plugin-sdk/src/store';
import { resolveEncodedCodeRefs } from '../coderefs/coderef-resolver';
import { remoteEntryFile } from '../constants';
import { ConsolePluginManifestJSON } from '../schema/plugin-manifest';
import { overrideSharedModules } from '../shared-modules';
import { overrideSharedModules } from '../shared-modules-override';
import { RemoteEntryModule } from '../types';
import { resolveURL } from '../utils/url';
import { fetchPluginManifest } from './plugin-manifest';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const validatePluginManifestSchema = async (
manifest: ConsolePluginManifestJSON,
manifestURL: string,
) => {
const schema = (await import('../../schema/plugin-manifest')).default;
const schema = (await import('../../generated/schema/plugin-manifest')).default;

// Use dynamic import to avoid pulling ajv dependency tree into main vendors chunk
const SchemaValidator = await import(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* eslint-disable global-require */
/* eslint-disable @typescript-eslint/no-require-imports */

import { RemoteEntryModule } from './types';

/**
* At runtime, Console will override (i.e. enforce Console-bundled implementation of) shared
* modules for each dynamic plugin, before loading any of the modules exposed by that plugin.
*
* This way, a single version of React etc. is used by Console application and its plugins.
*/
export const overrideSharedModules = (entryModule: RemoteEntryModule) => {
entryModule.override({
'@openshift-console/dynamic-plugin-sdk': async () => () =>
require('@console/dynamic-plugin-sdk/src/lib-core'),
'@openshift-console/dynamic-plugin-sdk-internal': async () => () =>
require('@console/dynamic-plugin-sdk/src/lib-internal'),
react: async () => () => require('react'),
'react-helmet': async () => () => require('react-helmet'),
'react-i18next': async () => () => require('react-i18next'),
'react-router': async () => () => require('react-router'),
'react-router-dom': async () => () => require('react-router-dom'),
});
};
Loading

0 comments on commit 45811f4

Please sign in to comment.