Skip to content

Commit

Permalink
feat(cli): implement rc CLI for registry (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
unadlib authored Mar 14, 2024
1 parent e967eb5 commit 24024ff
Show file tree
Hide file tree
Showing 12 changed files with 2,561 additions and 1,923 deletions.
15 changes: 15 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# @ringcentral/mfe-cli

![Node CI](https://github.com/ringcentral/ringcentral-mfe/workflows/Node%20CI/badge.svg)

A micro frontends framework for building Web applications

## Usage

```bash
npm install @ringcentral/mfe-cli
# or
yarn add @ringcentral/mfe-cli
```

You can visit [https://github.com/ringcentral/ringcentral-mfe](https://github.com/ringcentral/ringcentral-mfe) for more documentation.
3 changes: 3 additions & 0 deletions packages/cli/bin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node
// eslint-disable-next-line import/extensions
require('../dist/index.js').cli();
6 changes: 6 additions & 0 deletions packages/cli/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const baseConfig = require('../../jest.config');

module.exports = {
...baseConfig,
};
53 changes: 53 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "@ringcentral/mfe-cli",
"version": "0.3.0",
"description": "A micro frontends framework for building Web applications",
"main": "dist/index.js",
"module": "dist/index.mjs",
"typings": "dist/index.d.ts",
"bin": {
"mfe": "bin/index.js"
},
"files": [
"bin",
"dist"
],
"publishConfig": {
"access": "public"
},
"scripts": {
"build": "rollup -c ../../rollup.config.ts --configPlugin typescript",
"tsc": "tsc --noEmit",
"ci:test": "jest",
"watch": "",
"clean": "rimraf dist",
"prebuild": "yarn clean",
"test": "jest --watch"
},
"peerDependencies": {
"@ringcentral/mfe-shared": "^0.3.0"
},
"devDependencies": {
"@types/node-fetch": "2.6.3"
},
"dependencies": {
"@ringcentral/mfe-shared": "^0.3.0",
"commander": "10.0.1",
"fs-extra": "11.1.1",
"node-fetch": "^2.6.9",
"yargs": "17.6.2"
},
"bugs": {
"url": "https://github.com/ringcentral/ringcentral-mfe/issues"
},
"homepage": "https://github.com/ringcentral/ringcentral-mfe#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/ringcentral/ringcentral-mfe.git"
},
"keywords": [
"Micro Frontends"
],
"author": "RingCentral",
"license": "MIT"
}
18 changes: 18 additions & 0 deletions packages/cli/src/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Command } from 'commander';
import { FetchOptions, fetchInfo } from './utils';

export const createFetchCommand = (command: Command) => {
command
.command('fetch')
.option('-t, --type <type>', `a MFE type, 'app' | 'module'`, 'app')
.requiredOption('-n, --name <name>', `a MFE name`)
.requiredOption('-v, --version <version>', `a MFE version`)
.requiredOption('-r, --registry <registry>', `a MFE registry`)
.action(async ({ name, version, registry, type }: FetchOptions) => {
const result = await fetchInfo({ name, version, registry, type });
console.log('Fetch MFE options:');
console.log(JSON.stringify({ name, version, registry, type }, null, 2));
console.log('Fetch MFE result:');
console.log(JSON.stringify(result, null, 2));
});
};
18 changes: 18 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Command } from 'commander';
import path from 'path';
import fs from 'fs-extra';
import { createFetchCommand } from './fetch';

export const cli = () => {
const packageJson = fs.readJsonSync(
path.resolve(__dirname, '../package.json')
);
const command = new Command() as Command;
command.usage('[command] [options]').version(packageJson.version);

createFetchCommand(command);

command.parse(process.argv);
};

export * from './utils/index';
5 changes: 5 additions & 0 deletions packages/cli/src/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface PackageJson {
name?: string;
version?: string;
dependencies?: Record<string, string>;
}
131 changes: 131 additions & 0 deletions packages/cli/src/utils/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/no-shadow */
import fetch from 'node-fetch';

export interface FetchOptions {
name: string;
version: string;
registry: string;
type: 'app' | 'module';
}

interface DepData {
dependencies: Record<string, string>;
version: string;
meta: Record<string, unknown>;
variants: {
remoteEntry: string;
metadata: Record<string, unknown>;
}[];
}

export interface ModuleInfo {
entry: string;
version: string;
meta: Record<string, unknown>;
}

const fetchWithModule = async ({
registry,
name,
version,
deps,
}: {
registry: string;
name: string;
version: string;
deps: Record<string, ModuleInfo[]>;
}) => {
if (deps[name]) return;
const url = `${registry}/api/modules/${name}/releases/${version}`;
const response = await fetch(url);
const moduleData: DepData = await response.json();
deps[name] = moduleData.variants.map((item) => ({
entry: item.remoteEntry,
version: moduleData.version,
meta: {
...moduleData.meta,
...item.metadata,
},
}));
for (const [subModuleName, subVersion] of Object.entries(
moduleData.dependencies
) as [string, string][]) {
if (!deps[subModuleName]) {
await fetchWithModule({
deps,
name: subModuleName,
version: subVersion,
registry,
});
}
}
};

export const fetchInfo = async ({
name,
version,
registry,
type,
}: FetchOptions) => {
if (!name || !version || !registry) {
throw new Error(`'name' and 'version' are required.`);
}
if (type !== 'app' && type !== 'module') {
throw new Error(
`'type' can not be '${type}', it should be 'app' or 'module'.`
);
}
const isApp = type === 'app';
const data: {
name: string;
version: string;
registry: string;
type: 'app' | 'module';
dependencyLocks: Record<string, ModuleInfo[]>;
} = {
name,
version,
registry,
type,
dependencyLocks: {},
};
if (isApp) {
const url = `${registry}/api/apps/${name}/releases/${version}`;
const response = await fetch(url);
const result = await response.json();
for (const [moduleName, moduleInfo] of Object.entries(
result.dependencyLocks
) as [string, DepData][]) {
data.dependencyLocks[moduleName] = moduleInfo.variants.map((item) => ({
entry: item.remoteEntry,
version: moduleInfo.version,
meta: {
...moduleInfo.meta,
...item.metadata,
},
}));
for (const [moduleName, version] of Object.entries(
moduleInfo.dependencies
) as [string, string][]) {
if (!data.dependencyLocks[moduleName]) {
await fetchWithModule({
deps: data.dependencyLocks,
name: moduleName,
version,
registry,
});
}
}
}
} else {
await fetchWithModule({
name,
version,
registry,
deps: data.dependencyLocks,
});
}
delete data.dependencyLocks[name];
return data;
};
1 change: 1 addition & 0 deletions packages/cli/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './fetch';
3 changes: 3 additions & 0 deletions packages/cli/test/init.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test('basic', () => {
//
});
4 changes: 4 additions & 0 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src/**/*", "../../global.d.ts"]
}
Loading

0 comments on commit 24024ff

Please sign in to comment.