From df35c6a0b4c803e68f7a6ab4e256478f34a7e216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E5=B8=8C?= Date: Mon, 10 Aug 2020 22:03:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20demo-server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + packages/demo-server/.eslintrc.js | 24 ++++++ packages/demo-server/.prettierrc | 4 + packages/demo-server/README.md | 75 ++++++++++++++++++ packages/demo-server/nest-cli.json | 4 + packages/demo-server/package.json | 67 ++++++++++++++++ .../src/api/api.controller.spec.ts | 18 +++++ .../demo-server/src/api/api.controller.ts | 21 +++++ .../demo-server/src/api/api.service.spec.ts | 18 +++++ packages/demo-server/src/api/api.service.ts | 30 ++++++++ .../demo-server/src/app.controller.spec.ts | 22 ++++++ packages/demo-server/src/app.controller.ts | 12 +++ packages/demo-server/src/app.module.ts | 12 +++ packages/demo-server/src/app.service.ts | 8 ++ .../src/dto/generate-project.dto.spec.ts | 7 ++ .../src/dto/generate-project.dto.ts | 3 + packages/demo-server/src/main.ts | 9 +++ packages/demo-server/src/publisher/index.ts | 76 +++++++++++++++++++ packages/demo-server/src/publisher/utils.ts | 57 ++++++++++++++ packages/demo-server/test/app.e2e-spec.ts | 24 ++++++ packages/demo-server/test/jest-e2e.json | 9 +++ packages/demo-server/tsconfig.build.json | 4 + packages/demo-server/tsconfig.json | 15 ++++ packages/demo/src/editor/plugins/codeout.tsx | 2 +- packages/react-renderer/package.json | 2 +- scripts/start-server.sh | 5 ++ 26 files changed, 527 insertions(+), 2 deletions(-) create mode 100644 packages/demo-server/.eslintrc.js create mode 100644 packages/demo-server/.prettierrc create mode 100644 packages/demo-server/README.md create mode 100644 packages/demo-server/nest-cli.json create mode 100644 packages/demo-server/package.json create mode 100644 packages/demo-server/src/api/api.controller.spec.ts create mode 100644 packages/demo-server/src/api/api.controller.ts create mode 100644 packages/demo-server/src/api/api.service.spec.ts create mode 100644 packages/demo-server/src/api/api.service.ts create mode 100644 packages/demo-server/src/app.controller.spec.ts create mode 100644 packages/demo-server/src/app.controller.ts create mode 100644 packages/demo-server/src/app.module.ts create mode 100644 packages/demo-server/src/app.service.ts create mode 100644 packages/demo-server/src/dto/generate-project.dto.spec.ts create mode 100644 packages/demo-server/src/dto/generate-project.dto.ts create mode 100644 packages/demo-server/src/main.ts create mode 100644 packages/demo-server/src/publisher/index.ts create mode 100644 packages/demo-server/src/publisher/utils.ts create mode 100644 packages/demo-server/test/app.e2e-spec.ts create mode 100644 packages/demo-server/test/jest-e2e.json create mode 100644 packages/demo-server/tsconfig.build.json create mode 100644 packages/demo-server/tsconfig.json create mode 100755 scripts/start-server.sh diff --git a/package.json b/package.json index b3c803aea..c23c57a36 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "pub": "lerna publish --cd-version patch", "setup": "./scripts/setup.sh", "start": "./scripts/start.sh", + "start:server": "./scripts/start-server.sh", "test": "lerna run test --stream", "test:snapshot": "lerna run test:snapshot" }, diff --git a/packages/demo-server/.eslintrc.js b/packages/demo-server/.eslintrc.js new file mode 100644 index 000000000..0ae17cac2 --- /dev/null +++ b/packages/demo-server/.eslintrc.js @@ -0,0 +1,24 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: [ + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'prettier', + 'prettier/@typescript-eslint', + ], + root: true, + env: { + node: true, + jest: true, + }, + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, +}; diff --git a/packages/demo-server/.prettierrc b/packages/demo-server/.prettierrc new file mode 100644 index 000000000..dcb72794f --- /dev/null +++ b/packages/demo-server/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/packages/demo-server/README.md b/packages/demo-server/README.md new file mode 100644 index 000000000..a59ef9241 --- /dev/null +++ b/packages/demo-server/README.md @@ -0,0 +1,75 @@ +

+ Nest Logo +

+ +[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master +[travis-url]: https://travis-ci.org/nestjs/nest +[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux +[linux-url]: https://travis-ci.org/nestjs/nest + +

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

+

+NPM Version +Package License +NPM Downloads +Travis +Linux +Coverage +Gitter +Backers on Open Collective +Sponsors on Open Collective + + +

+ + +## Description + +[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. + +## Installation + +```bash +$ npm install +``` + +## Running the app + +```bash +# development +$ npm run start + +# watch mode +$ npm run start:dev + +# production mode +$ npm run start:prod +``` + +## Test + +```bash +# unit tests +$ npm run test + +# e2e tests +$ npm run test:e2e + +# test coverage +$ npm run test:cov +``` + +## Support + +Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). + +## Stay in touch + +- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) +- Website - [https://nestjs.com](https://nestjs.com/) +- Twitter - [@nestframework](https://twitter.com/nestframework) + +## License + + Nest is [MIT licensed](LICENSE). diff --git a/packages/demo-server/nest-cli.json b/packages/demo-server/nest-cli.json new file mode 100644 index 000000000..56167b36a --- /dev/null +++ b/packages/demo-server/nest-cli.json @@ -0,0 +1,4 @@ +{ + "collection": "@nestjs/schematics", + "sourceRoot": "src" +} diff --git a/packages/demo-server/package.json b/packages/demo-server/package.json new file mode 100644 index 000000000..8c96c0ec6 --- /dev/null +++ b/packages/demo-server/package.json @@ -0,0 +1,67 @@ +{ + "name": "@ali/lowcode-demo-server", + "version": "0.8.37", + "private": true, + "description": "低代码引擎 DEMO Server 端", + "scripts": { + "prebuild": "rimraf dist", + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "start": "nest start", + "start:debug": "nest start --debug --watch", + "start:dev": "nest start --watch", + "start:prod": "node dist/main", + "test": "jest", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json", + "test:watch": "jest --watch" + }, + "jest": { + "coverageDirectory": "../coverage", + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testEnvironment": "node", + "testRegex": ".spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } + }, + "dependencies": { + "@ali/lowcode-code-generator": "0.8.8", + "@nestjs/common": "^7.0.0", + "@nestjs/core": "^7.0.0", + "@nestjs/platform-express": "^7.0.0", + "jszip": "^3.5.0", + "reflect-metadata": "^0.1.13", + "rimraf": "^3.0.2", + "rxjs": "^6.5.4" + }, + "devDependencies": { + "@nestjs/cli": "^7.0.0", + "@nestjs/schematics": "^7.0.0", + "@nestjs/testing": "^7.0.0", + "@types/express": "^4.17.3", + "@types/jest": "25.2.3", + "@types/node": "^13.9.1", + "@types/supertest": "^2.0.8", + "@typescript-eslint/eslint-plugin": "3.0.2", + "@typescript-eslint/parser": "3.0.2", + "eslint": "7.1.0", + "eslint-config-prettier": "^6.10.0", + "eslint-plugin-import": "^2.20.1", + "jest": "26.0.1", + "prettier": "^1.19.1", + "supertest": "^4.0.2", + "ts-jest": "26.1.0", + "ts-loader": "^6.2.1", + "ts-node": "^8.6.2", + "tsconfig-paths": "^3.9.0", + "typescript": "^3.7.4" + } +} diff --git a/packages/demo-server/src/api/api.controller.spec.ts b/packages/demo-server/src/api/api.controller.spec.ts new file mode 100644 index 000000000..2103b3840 --- /dev/null +++ b/packages/demo-server/src/api/api.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ApiController } from './api.controller'; + +describe('Api Controller', () => { + let controller: ApiController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ApiController], + }).compile(); + + controller = module.get(ApiController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/packages/demo-server/src/api/api.controller.ts b/packages/demo-server/src/api/api.controller.ts new file mode 100644 index 000000000..c7a23ec5c --- /dev/null +++ b/packages/demo-server/src/api/api.controller.ts @@ -0,0 +1,21 @@ +import { Controller, Res, Get, Post, Body, Header } from '@nestjs/common'; +import { Response } from 'express'; +import { ApiService } from './api.service'; +import { GenerateProjectDto } from '../dto/generate-project.dto'; + +@Controller('api') +export class ApiController { + constructor(private readonly apiService: ApiService) {} + + @Get('generate/test') + generateTest() { + return 'generate test'; + } + + @Post('generate/project') + @Header('Content-Type', 'application/zip') + async generateProject(@Res() res: Response, @Body() dto: GenerateProjectDto) { + const file = await this.apiService.generateProject(dto.schema); + file.pipe(res); + } +} diff --git a/packages/demo-server/src/api/api.service.spec.ts b/packages/demo-server/src/api/api.service.spec.ts new file mode 100644 index 000000000..fae6fe722 --- /dev/null +++ b/packages/demo-server/src/api/api.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ApiService } from './api.service'; + +describe('ApiService', () => { + let service: ApiService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ApiService], + }).compile(); + + service = module.get(ApiService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/packages/demo-server/src/api/api.service.ts b/packages/demo-server/src/api/api.service.ts new file mode 100644 index 000000000..47af7af3d --- /dev/null +++ b/packages/demo-server/src/api/api.service.ts @@ -0,0 +1,30 @@ +import * as os from 'os'; +import * as path from 'path'; +import * as fs from 'fs'; +import { Injectable } from '@nestjs/common'; +import CodeGenerator from '@ali/lowcode-code-generator'; +import { createZipPublisher } from '../publisher'; + +@Injectable() +export class ApiService { + async generateProject(schema: string) { + const tmpDir = os.tmpdir(); + const createIceJsProjectBuilder = CodeGenerator.solutions.icejs; + const builder = createIceJsProjectBuilder(); + const publisher = createZipPublisher({ + outputPath: tmpDir, + projectSlug: 'demo-project', + }); + const filePath = path.join(tmpDir, 'demo-project.zip'); + + const result = await builder.generateProject(schema); + publisher.setProject(result); + const response = await publisher.publish(); + if (!response.success) { + throw new Error('generateProject failed'); + } + return fs.createReadStream(filePath); + + // return filePath; + } +} diff --git a/packages/demo-server/src/app.controller.spec.ts b/packages/demo-server/src/app.controller.spec.ts new file mode 100644 index 000000000..d22f3890a --- /dev/null +++ b/packages/demo-server/src/app.controller.spec.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +describe('AppController', () => { + let appController: AppController; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); + + appController = app.get(AppController); + }); + + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); +}); diff --git a/packages/demo-server/src/app.controller.ts b/packages/demo-server/src/app.controller.ts new file mode 100644 index 000000000..cce879ee6 --- /dev/null +++ b/packages/demo-server/src/app.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get() + getHello(): string { + return this.appService.getHello(); + } +} diff --git a/packages/demo-server/src/app.module.ts b/packages/demo-server/src/app.module.ts new file mode 100644 index 000000000..4b3b55608 --- /dev/null +++ b/packages/demo-server/src/app.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { ApiController } from './api/api.controller'; +import { ApiService } from './api/api.service'; + +@Module({ + imports: [], + controllers: [AppController, ApiController], + providers: [AppService, ApiService], +}) +export class AppModule {} diff --git a/packages/demo-server/src/app.service.ts b/packages/demo-server/src/app.service.ts new file mode 100644 index 000000000..927d7cca0 --- /dev/null +++ b/packages/demo-server/src/app.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} diff --git a/packages/demo-server/src/dto/generate-project.dto.spec.ts b/packages/demo-server/src/dto/generate-project.dto.spec.ts new file mode 100644 index 000000000..fb4b92f3a --- /dev/null +++ b/packages/demo-server/src/dto/generate-project.dto.spec.ts @@ -0,0 +1,7 @@ +import { GenerateProjectDto } from './generate-project.dto'; + +describe('GenerateProjectDto', () => { + it('should be defined', () => { + expect(new GenerateProjectDto()).toBeDefined(); + }); +}); diff --git a/packages/demo-server/src/dto/generate-project.dto.ts b/packages/demo-server/src/dto/generate-project.dto.ts new file mode 100644 index 000000000..70c1ba297 --- /dev/null +++ b/packages/demo-server/src/dto/generate-project.dto.ts @@ -0,0 +1,3 @@ +export class GenerateProjectDto { + schema: string; +} diff --git a/packages/demo-server/src/main.ts b/packages/demo-server/src/main.ts new file mode 100644 index 000000000..1b1812cdb --- /dev/null +++ b/packages/demo-server/src/main.ts @@ -0,0 +1,9 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + app.enableCors(); + await app.listen(7002); +} +bootstrap(); diff --git a/packages/demo-server/src/publisher/index.ts b/packages/demo-server/src/publisher/index.ts new file mode 100644 index 000000000..3bd62d4eb --- /dev/null +++ b/packages/demo-server/src/publisher/index.ts @@ -0,0 +1,76 @@ +import { IResultDir } from '@ali/lowcode-code-generator'; +import { isNodeProcess, writeZipToDisk, generateProjectZip } from './utils' + +export type PublisherFactory = (configuration?: Partial) => U; + +export interface IPublisher { + publish: (options?: T) => Promise>; + getProject: () => IResultDir | void; + setProject: (project: IResultDir) => void; +} + +export interface IPublisherFactoryParams { + project?: IResultDir; +} +export interface IPublisherResponse { + success: boolean; + payload?: T; +} + +declare type ZipPublisherResponse = string | Buffer | Blob + +export interface ZipFactoryParams extends IPublisherFactoryParams { + outputPath?: string + projectSlug?: string +} + +export interface ZipPublisher extends IPublisher { + getOutputPath: () => string + setOutputPath: (path: string) => void +} + +export const createZipPublisher: PublisherFactory = ( + params: ZipFactoryParams = {} +): ZipPublisher => { + let { project, outputPath } = params + + const getProject = () => project + const setProject = (projectToSet: IResultDir) => { + project = projectToSet + } + + const getOutputPath = () => outputPath + const setOutputPath = (path: string) => { + outputPath = path + } + + const publish = async (options: ZipFactoryParams = {}) => { + const projectToPublish = options.project || project + if (!projectToPublish) { + throw new Error('MissingProject'); + } + + const zipName = options.projectSlug || params.projectSlug || projectToPublish.name + + try { + const zipContent = await generateProjectZip(projectToPublish) + + // If not output path is provided, zip is not written to disk + const projectOutputPath = options.outputPath || outputPath + if (projectOutputPath && isNodeProcess()) { + await writeZipToDisk(projectOutputPath, zipContent, zipName) + } + return { success: true, payload: zipContent } + } catch (error) { + throw new Error('ZipUnexpected'); + } + } + + return { + publish, + getProject, + setProject, + getOutputPath, + setOutputPath, + } +} diff --git a/packages/demo-server/src/publisher/utils.ts b/packages/demo-server/src/publisher/utils.ts new file mode 100644 index 000000000..0b7b205ff --- /dev/null +++ b/packages/demo-server/src/publisher/utils.ts @@ -0,0 +1,57 @@ +import * as JSZip from 'jszip'; +import { IResultFile, IResultDir } from '@ali/lowcode-code-generator'; + +export const isNodeProcess = (): boolean => { + return ( + typeof process === 'object' && + typeof process.versions === 'object' && + typeof process.versions.node !== 'undefined' + ) +} + +export const writeZipToDisk = ( + zipFolderPath: string, + content: Buffer | Blob, + zipName: string +): void => { + const fs = require('fs') + const path = require('path') + + if (!fs.existsSync(zipFolderPath)) { + fs.mkdirSync(zipFolderPath, { recursive: true }) + } + + const zipPath = path.join(zipFolderPath, `${zipName}.zip`) + + const writeStream = fs.createWriteStream(zipPath) + writeStream.write(content) + writeStream.end() +} + +export const generateProjectZip = async (project: IResultDir): Promise => { + let zip = new JSZip() + zip = writeFolderToZip(project, zip, true) + const zipType = isNodeProcess() ? 'nodebuffer' : 'blob' + return zip.generateAsync({ type: zipType }) +} + +const writeFolderToZip = ( + folder: IResultDir, + parentFolder: JSZip, + ignoreFolder: boolean = false +) => { + const zipFolder = ignoreFolder ? parentFolder : parentFolder.folder(folder.name) + + folder.files.forEach((file: IResultFile) => { + // const options = file.contentEncoding === 'base64' ? { base64: true } : {} + const options = {}; + const fileName = file.ext ? `${file.name}.${file.ext}` : file.name + zipFolder.file(fileName, file.content, options) + }) + + folder.dirs.forEach((subFolder: IResultDir) => { + writeFolderToZip(subFolder, zipFolder) + }) + + return parentFolder +} diff --git a/packages/demo-server/test/app.e2e-spec.ts b/packages/demo-server/test/app.e2e-spec.ts new file mode 100644 index 000000000..50cda6233 --- /dev/null +++ b/packages/demo-server/test/app.e2e-spec.ts @@ -0,0 +1,24 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import * as request from 'supertest'; +import { AppModule } from './../src/app.module'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); +}); diff --git a/packages/demo-server/test/jest-e2e.json b/packages/demo-server/test/jest-e2e.json new file mode 100644 index 000000000..e9d912f3e --- /dev/null +++ b/packages/demo-server/test/jest-e2e.json @@ -0,0 +1,9 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } +} diff --git a/packages/demo-server/tsconfig.build.json b/packages/demo-server/tsconfig.build.json new file mode 100644 index 000000000..64f86c6bd --- /dev/null +++ b/packages/demo-server/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/packages/demo-server/tsconfig.json b/packages/demo-server/tsconfig.json new file mode 100644 index 000000000..bf10a2398 --- /dev/null +++ b/packages/demo-server/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "es2017", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true + } +} diff --git a/packages/demo/src/editor/plugins/codeout.tsx b/packages/demo/src/editor/plugins/codeout.tsx index 7d2c127f4..c2046bd39 100644 --- a/packages/demo/src/editor/plugins/codeout.tsx +++ b/packages/demo/src/editor/plugins/codeout.tsx @@ -21,7 +21,7 @@ interface BasicSection { [k: string]: any; } -const CODEOUT_SERVICE_HOST = '30.13.88.126:3000'; +const CODEOUT_SERVICE_HOST = 'localhost:7002'; const Codeout = ({ editor }: PluginProps) => { const handleClick = () => { diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json index b529d7274..f1c21c3fe 100644 --- a/packages/react-renderer/package.json +++ b/packages/react-renderer/package.json @@ -53,5 +53,5 @@ "publishConfig": { "registry": "http://registry.npm.alibaba-inc.com" }, - "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.8.13/build/index.html" + "homepage": "https:/unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.8.13/build/index.html" } diff --git a/scripts/start-server.sh b/scripts/start-server.sh new file mode 100755 index 000000000..0b904d722 --- /dev/null +++ b/scripts/start-server.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +# FIXME! do not run build +lerna exec --scope @ali/lowcode-code-generator -- npm run build +lerna exec --scope @ali/lowcode-demo-server -- npm start