diff --git a/bin/test.sh b/bin/test.sh index 3e28d962..07dca659 100755 --- a/bin/test.sh +++ b/bin/test.sh @@ -15,3 +15,5 @@ test() { test body-parser-example test helloworld test hello-tegg +test hackernews-typescript +test hackernews-tegg diff --git a/hackernews-async-ts-di/.autod.conf.js b/hackernews-async-ts-di/.autod.conf.js deleted file mode 100644 index 4d1fb4ad..00000000 --- a/hackernews-async-ts-di/.autod.conf.js +++ /dev/null @@ -1,26 +0,0 @@ -'ues strict'; - -module.exports = { - write: true, - plugin: 'autod-egg', - prefix: '^', - devprefix: '^', - exclude: [ - 'test/fixtures', - ], - dep: [ - 'egg', - ], - devdep: [ - 'autod', - 'autod-egg', - 'egg-bin', - ], - keep: [ - 'tslib', - 'typescript', - ], - semver: [ - ], - test: 'scripts', -}; diff --git a/hackernews-async-ts-di/.gitignore b/hackernews-async-ts-di/.gitignore deleted file mode 100644 index faf57fa0..00000000 --- a/hackernews-async-ts-di/.gitignore +++ /dev/null @@ -1,71 +0,0 @@ -app/**/*.js -test/**/*.js -config/**/*.js -*.map -run -logs - -# Created by https://www.gitignore.io/api/node - -### Node ### -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Typescript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env - - -# End of https://www.gitignore.io/api/node diff --git a/hackernews-async-ts-di/.vscode/settings.json b/hackernews-async-ts-di/.vscode/settings.json deleted file mode 100644 index db221e08..00000000 --- a/hackernews-async-ts-di/.vscode/settings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "files.exclude": { - "USE_GITIGNORE": true, - "**/*.js": { - "when": "$(basename).ts" - }, - "**/*.map": true, - "run": true, - "logs": true, - "out": true, - "node_modules": true - } -} diff --git a/hackernews-async-ts-di/README.md b/hackernews-async-ts-di/README.md deleted file mode 100644 index dded0eb1..00000000 --- a/hackernews-async-ts-di/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# egg-example-hackernews-async - -[Hacker News](https://news.ycombinator.com/) showcase using async/await for egg - -## QuickStart - -### Development -```shell -$ npm install -$ npm run tsc:w -$ npm run dev -$ open http://localhost:7001/ -``` - -### Deploy - -Use `EGG_SERVER_ENV=prod` to enable prod mode - -```shell -$ EGG_SERVER_ENV=prod npm start -``` - -### Npm Scripts - -- Use `npm run autod` to auto detect dependencies upgrade -- Use `npm run lint` to check code style -- Use `npm test` to run unit test - -### Requirement - -Please ensure your node version is `>=7.6.0` for async await support without flag. If your node version is `>=7.0.0 < 7.6.0`, you can run npm scripts with harmony flag - -```shell -# start server -npm run dev -- --harmony-async-await -# run test cases -npm run test-local -- --harmony-async-await -``` diff --git a/hackernews-async-ts-di/app/controller/index.d.ts b/hackernews-async-ts-di/app/controller/index.d.ts deleted file mode 100644 index 0b8bebb8..00000000 --- a/hackernews-async-ts-di/app/controller/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import NewsController from './news'; -declare module 'egg' { - export interface IController { - news: NewsController; - } -} diff --git a/hackernews-async-ts-di/app/extend/filter.ts b/hackernews-async-ts-di/app/extend/filter.ts deleted file mode 100644 index e74123e1..00000000 --- a/hackernews-async-ts-di/app/extend/filter.ts +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -import * as moment from 'moment'; - -exports.relativeTime = (time) => moment(new Date(time * 1000)).fromNow(); - -exports.domain = (url) => url && url.split('/')[2]; diff --git a/hackernews-async-ts-di/app/public/favicon.png b/hackernews-async-ts-di/app/public/favicon.png deleted file mode 100644 index 41d69cdc..00000000 Binary files a/hackernews-async-ts-di/app/public/favicon.png and /dev/null differ diff --git a/hackernews-async-ts-di/app/router.ts b/hackernews-async-ts-di/app/router.ts deleted file mode 100644 index 4d5d3113..00000000 --- a/hackernews-async-ts-di/app/router.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Application } from 'egg'; - -export default (app: Application) => { - const controller = app.controller; - app.redirect('/', '/news'); - app.router.get('/news', controller.news.list); - app.router.get('/news/item/:id', controller.news.detail); - app.router.get('/news/user/:id', controller.news.user); -}; diff --git a/hackernews-async-ts-di/config/config.ts b/hackernews-async-ts-di/config/config.ts deleted file mode 100644 index f15ec1c0..00000000 --- a/hackernews-async-ts-di/config/config.ts +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; -import { EggAppConfig } from 'egg'; -import * as fs from 'fs'; -import * as path from 'path'; -import 'source-map-support/register'; -import defaultConfig from './defaultConfig'; - -export default (appInfo: EggAppConfig) => { - const config: any = {}; - - // should change to your own - config.keys = appInfo.name + '123456'; - - config.siteFile = { - '/favicon.ico': fs.readFileSync(path.join(appInfo.baseDir, 'app/public/favicon.png')), - }; - - config.view = { - defaultViewEngine: 'nunjucks', - mapping: { - '.tpl': 'nunjucks', - }, - }; - - return { ...config, ...defaultConfig }; -}; diff --git a/hackernews-async-ts-di/config/defaultConfig.ts b/hackernews-async-ts-di/config/defaultConfig.ts deleted file mode 100644 index e6279b5b..00000000 --- a/hackernews-async-ts-di/config/defaultConfig.ts +++ /dev/null @@ -1,14 +0,0 @@ -export class DefaultConfig { - news = { - pageSize: 30, - serverUrl: 'https://hacker-news.firebaseio.com/v0', - }; -}; - -export default new DefaultConfig(); - -declare module 'egg' { - export interface Application { - config: EggAppConfig & DefaultConfig; - } -} diff --git a/hackernews-async-ts-di/config/plugin.ts b/hackernews-async-ts-di/config/plugin.ts deleted file mode 100644 index 31c30468..00000000 --- a/hackernews-async-ts-di/config/plugin.ts +++ /dev/null @@ -1,7 +0,0 @@ -exports.static = true; - -exports.nunjucks = { - enable: true, - package: 'egg-view-nunjucks', -}; - diff --git a/hackernews-async-ts-di/package.json b/hackernews-async-ts-di/package.json deleted file mode 100644 index 6de79cb8..00000000 --- a/hackernews-async-ts-di/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "egg-example-hackernews-async-ts", - "version": "1.0.0", - "description": "hackernews showcase using async/await for egg", - "private": true, - "dependencies": { - "egg": "^2.0.0", - "egg-di": "^1.0.0", - "egg-view-nunjucks": "^2.1.4", - "moment": "^2.19.4", - "source-map-support": "^0.5.0", - "tslib": "^1.8.1", - "typescript": "^2.6.2" - }, - "devDependencies": { - "@types/cheerio": "^0.22.1", - "@types/mocha": "^2.2.40", - "@types/node": "^7.0.12", - "@types/supertest": "^2.0.0", - "autod": "^3.0.1", - "autod-egg": "^1.1.0", - "cheerio": "^1.0.0-rc.2", - "egg-bin": "^4.3.7", - "egg-mock": "^3.14.0", - "rimraf": "^2.6.1", - "tslint": "^4.0.0" - }, - "engines": { - "node": ">=8.9.0" - }, - "scripts": { - "clean": "rimraf app/**/*.{js,map} test/**/*.{js,map} config/**/*.{js,map}", - "tsc": "tsc -p tsconfig.json", - "tsc:w": "tsc -p tsconfig.json -w", - "debug": "egg-bin debug", - "dev": "egg-bin dev", - "test": "npm run tsc && npm run test-local", - "test-local": "egg-bin test", - "cov": "egg-bin cov", - "lint": "tslint .", - "ci": "npm run lint && npm run cov", - "autod": "autod" - } -} diff --git a/hackernews-async-ts-di/test/app/controller/news.test.ts b/hackernews-async-ts-di/test/app/controller/news.test.ts deleted file mode 100644 index df93b568..00000000 --- a/hackernews-async-ts-di/test/app/controller/news.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - -import * as assert from 'assert'; -import * as cheerio from 'cheerio'; -import mm from 'egg-mock'; - -describe('test/app/controller/news.test.ts', () => { - const app = mm.app(); - before(async () => { - await app.ready(); - }); - - after(() => app.close()); - - afterEach(mm.restore); - - it('should GET /news', async () => { - const result = await app.httpRequest().get('/news').expect(200); - const $ = cheerio.load(result.text); - const listItem = $('.news-view .item'); - assert(listItem.length === app.config.news.pageSize); - }); - - it('should GET /news/item/:id', async () => { - await app.httpRequest() - .get('/news/item/1') - // just a example, use regex to test part of dom string, but should be strong characteristic - .expect(/\/news\/item\/1/) - .expect(200); - }); - - it('should GET /news/user/:id', async () => { - await app.httpRequest() - .get('/news/user/activatedgeek') - // just a example, use regex to test part of dom string, but should be strong characteristic - .expect(/user:<\/span> activatedgeek/) - .expect(200); - }); -}); diff --git a/hackernews-async-ts-di/test/app/service/HackerNews.test.ts b/hackernews-async-ts-di/test/app/service/HackerNews.test.ts deleted file mode 100644 index a183c477..00000000 --- a/hackernews-async-ts-di/test/app/service/HackerNews.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -import * as assert from 'assert'; -import { Context } from 'egg'; -import { getComponent } from 'egg-di'; -import mm from 'egg-mock'; -import { HackerNews } from '../../../app/service/HackerNews'; - -describe('test/app/service/HackerNews.test.js', () => { - const app = mm.app(); - let ctx: Context; - let hackerNews: HackerNews; - - before(async () => { - await app.ready(); - ctx = app.mockContext(); - hackerNews = getComponent(HackerNews, ctx); - }); - - after(() => app.close()); - afterEach(mm.restore); - - it('getTopStories', async () => { - const list = await hackerNews.getTopStories(); - assert(list.length === 30); - }); - - it('getItem', async () => { - const item = await hackerNews.getItem(1); - assert(item.id && item.title && item.url); - }); -}); diff --git a/hackernews-async-ts-di/tsconfig.json b/hackernews-async-ts-di/tsconfig.json deleted file mode 100644 index 05d10e45..00000000 --- a/hackernews-async-ts-di/tsconfig.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "compileOnSave": true, - "compilerOptions": { - "target": "es2017", - "module": "commonjs", - "noImplicitAny": false, - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "charset": "utf8", - "allowJs": false, - "pretty": true, - "noEmitOnError": false, - "noUnusedLocals": true, - "noUnusedParameters": true, - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "noFallthroughCasesInSwitch": true, - "skipLibCheck": true, - "skipDefaultLibCheck": true, - "inlineSourceMap": true, - "importHelpers": true - }, - "include": [ - "app/**/*", - "config/**/*", - "test/**/*.ts" - ], - "exclude": [ - "app/public", - "app/views" - ] -} diff --git a/hackernews-async-ts-di/tslint.json b/hackernews-async-ts-di/tslint.json deleted file mode 100644 index 73221c9f..00000000 --- a/hackernews-async-ts-di/tslint.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "extends": "tslint:latest", - "rules": { - "quotemark": [ - true, - "single", - "jsx-double" - ], - "no-console": [ - true, - "dir", - "log", - "error", - "warn" - ], - "space-before-function-paren": false, - "interface-name": [ - true, - "never-prefix" - ], - "adjacent-overload-signatures": true, - "member-access": [ - false - ], - "member-ordering": [ - true, - { - "order": "fields-first" - } - ], - "object-literal-sort-keys": false, - "max-classes-per-file": [ - true, - 10 - ], - "variable-name": [ - true, - "allow-leading-underscore" - ], - "align": [true, "statements"] - } -} - diff --git a/hackernews-async-ts/app/extend/filter.ts b/hackernews-async-ts/app/extend/filter.ts deleted file mode 100644 index 80813fce..00000000 --- a/hackernews-async-ts/app/extend/filter.ts +++ /dev/null @@ -1,9 +0,0 @@ -import moment from 'moment'; - -export function relativeTime(time) { - return moment(new Date(time * 1000)).fromNow(); -} - -export function domain(url) { - return url && url.split('/')[2]; -} diff --git a/hackernews-async-ts/app/public/favicon.png b/hackernews-async-ts/app/public/favicon.png deleted file mode 100644 index 41d69cdc..00000000 Binary files a/hackernews-async-ts/app/public/favicon.png and /dev/null differ diff --git a/hackernews-async-ts/config/config.default.ts b/hackernews-async-ts/config/config.default.ts deleted file mode 100644 index 64dfc022..00000000 --- a/hackernews-async-ts/config/config.default.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { EggAppConfig, PowerPartial } from 'egg'; -import * as fs from 'fs'; -import * as path from 'path'; - -// for config.{env}.ts -export type DefaultConfig = PowerPartial; - -// app special config scheme -export interface BizConfig { - sourceUrl: string; - news: { - pageSize: number; - serverUrl: string; - }; -} - -export default (appInfo: EggAppConfig) => { - const config = {} as PowerPartial & BizConfig; - - // app special config - config.sourceUrl = `https://github.com/eggjs/examples/tree/master/${appInfo.name}`; - config.news = { - pageSize: 30, - serverUrl: 'https://hacker-news.firebaseio.com/v0', - }; - - // override config from framework / plugin - config.keys = appInfo.name + '123456'; - - config.view = { - defaultViewEngine: 'nunjucks', - mapping: { - '.tpl': 'nunjucks', - }, - }; - - config.siteFile = { - '/favicon.ico': fs.readFileSync(path.join(appInfo.baseDir, 'app/public/favicon.png')), - }; - - return config; -}; diff --git a/hackernews-async-ts/config/config.local.ts b/hackernews-async-ts/config/config.local.ts deleted file mode 100644 index c790a638..00000000 --- a/hackernews-async-ts/config/config.local.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DefaultConfig } from './config.default'; - -export default () => { - const config: DefaultConfig = {}; - config.news = { - pageSize: 20, - }; - return config; -}; diff --git a/hackernews-async-ts/config/config.prod.ts b/hackernews-async-ts/config/config.prod.ts deleted file mode 100644 index a1cb3447..00000000 --- a/hackernews-async-ts/config/config.prod.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DefaultConfig } from './config.default'; - -export default () => { - const config: DefaultConfig = {}; - config.news = { - pageSize: 30, - }; - return config; -}; diff --git a/hackernews-async-ts/package.json b/hackernews-async-ts/package.json deleted file mode 100644 index 237a5b99..00000000 --- a/hackernews-async-ts/package.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "hackernews-async-ts", - "version": "1.0.0", - "description": "hackernews showcase using typescript && egg", - "private": true, - "egg": { - "typescript": true - }, - "scripts": { - "start": "egg-scripts start", - "dev": "egg-bin dev", - "debug": "egg-bin debug", - "test-local": "egg-bin test", - "test": "npm run lint -- --fix && npm run test-local", - "cov": "egg-bin cov", - "tsc": "tsc -p tsconfig.json", - "ci": "npm run lint && npm run cov && npm run tsc", - "lint": "eslint .", - "clean": "tsc -b --clean" - }, - "dependencies": { - "egg": "^3.11.0", - "egg-scripts": "^2.17.0", - "egg-view-nunjucks": "^2.3.0", - "moment": "^2.22.0" - }, - "devDependencies": { - "@eggjs/tsconfig": "^1.1.0", - "@types/cheerio": "^0.22.1", - "@types/mocha": "^10.0.1", - "cheerio": "^1.0.0-rc.2", - "egg-bin": "^5.9.0", - "egg-mock": "^5.5.0", - "eslint": "^8.31.0", - "eslint-config-egg": "^12.1.0", - "typescript": "^4.9.4" - }, - "engines": { - "node": ">=16.0.0" - } -} diff --git a/hackernews-async-ts/.eslintrc b/hackernews-tegg/.eslintrc similarity index 100% rename from hackernews-async-ts/.eslintrc rename to hackernews-tegg/.eslintrc diff --git a/hackernews-async-ts/.gitignore b/hackernews-tegg/.gitignore similarity index 100% rename from hackernews-async-ts/.gitignore rename to hackernews-tegg/.gitignore diff --git a/hackernews-tegg/README.md b/hackernews-tegg/README.md new file mode 100644 index 00000000..a026f210 --- /dev/null +++ b/hackernews-tegg/README.md @@ -0,0 +1,33 @@ +# hackernews-tegg + +[Hacker News](https://news.ycombinator.com/) showcase using tegg + +## QuickStart + +### Development + +```bash +$ npm i +$ npm run dev +$ open http://localhost:7001/ +``` + +Don't tsc compile at development mode, if you had run `tsc` then you need to `npm run clean` before `npm run dev`. + +### Deploy + +```bash +$ npm run tsc +$ npm start +``` + +### Npm Scripts + +- Use `npm run lint` to check code style +- Use `npm test` to run unit test +- se `npm run clean` to clean compiled js at development mode once + +### Requirement + +- Node.js 22.x +- Typescript 5.x diff --git a/hackernews-async-ts/app/controller/news.ts b/hackernews-tegg/app/controller/news.ts similarity index 84% rename from hackernews-async-ts/app/controller/news.ts rename to hackernews-tegg/app/controller/news.ts index 2bbd958d..4a359d49 100644 --- a/hackernews-async-ts/app/controller/news.ts +++ b/hackernews-tegg/app/controller/news.ts @@ -6,7 +6,7 @@ export default class NewsController extends Controller { const pageSize = app.config.news.pageSize; const page = parseInt(ctx.query.page, 10) || 1; - const idList = await ctx.service.news.getTopStories(page); + const idList = await ctx.service.news.getTopStories(page) as number[]; // get itemInfo parallel const newsList = await Promise.all(idList.map(id => ctx.service.news.getItem(id))); @@ -15,7 +15,7 @@ export default class NewsController extends Controller { public async detail() { const { ctx } = this; - const id = ctx.params.id; + const id = ctx.params!.id as unknown as number; const newsInfo = await ctx.service.news.getItem(id); // get comment parallel const commentList = await Promise.all(newsInfo.kids.map(_id => ctx.service.news.getItem(_id))); @@ -24,7 +24,7 @@ export default class NewsController extends Controller { public async user() { const { ctx } = this; - const id = ctx.params.id; + const id = ctx.params!.id as unknown as number; const userInfo = await ctx.service.news.getUser(id); await ctx.render('news/user.tpl', { user: userInfo }); } diff --git a/hackernews-tegg/app/extend/filter.ts b/hackernews-tegg/app/extend/filter.ts new file mode 100644 index 00000000..4bd566f6 --- /dev/null +++ b/hackernews-tegg/app/extend/filter.ts @@ -0,0 +1,9 @@ +import moment from 'moment'; + +export function relativeTime(seconds: number) { + return moment(new Date(seconds * 1000)).fromNow(); +} + +export function domain(url: string) { + return url && url.split('/')[2]; +} diff --git a/hackernews-async-ts-di/app/public/css/news.css b/hackernews-tegg/app/public/css/news.css similarity index 100% rename from hackernews-async-ts-di/app/public/css/news.css rename to hackernews-tegg/app/public/css/news.css diff --git a/hackernews-async-ts/app/router.ts b/hackernews-tegg/app/router.ts similarity index 87% rename from hackernews-async-ts/app/router.ts rename to hackernews-tegg/app/router.ts index cead0f3c..4b6e8b23 100644 --- a/hackernews-async-ts/app/router.ts +++ b/hackernews-tegg/app/router.ts @@ -3,7 +3,7 @@ import { Application } from 'egg'; export default (app: Application) => { const { controller, router } = app; - router.redirect('/', '/news'); + router.redirect('/', '/news', 302); router.get('/news', controller.news.list); router.get('/news/item/:id', controller.news.detail); router.get('/news/user/:id', controller.news.user); diff --git a/hackernews-async-ts/app/service/News.ts b/hackernews-tegg/app/service/News.ts similarity index 80% rename from hackernews-async-ts/app/service/News.ts rename to hackernews-tegg/app/service/News.ts index ffef8d5c..f0663eaa 100644 --- a/hackernews-async-ts/app/service/News.ts +++ b/hackernews-tegg/app/service/News.ts @@ -21,14 +21,14 @@ export class HackerNews extends Service { * @param api - Api name * @param opts - urllib options */ - public async request(api: string, opts?: any) { - const options = { + public async request(api: string, opts?: any): Promise { + const url = `${this.config.news.serverUrl}/${api}`; + const result = await this.ctx.httpclient.request(url, { dataType: 'json', - timeout: '30s', + timeout: 30000, ...opts, - }; - - const result = await this.ctx.curl(`${this.config.news.serverUrl}/${api}`, options); + }); + this.logger.info('request %s got data %j', url, result.data); return result.data; } @@ -61,7 +61,7 @@ export class HackerNews extends Service { * @param id - itemId */ public async getItem(id: number): Promise { - return await this.request(`item/${id}.json`); + return await this.request(`item/${id}.json`); } /** diff --git a/hackernews-async-ts/app/view/layout/layout.tpl b/hackernews-tegg/app/view/layout/layout.tpl similarity index 81% rename from hackernews-async-ts/app/view/layout/layout.tpl rename to hackernews-tegg/app/view/layout/layout.tpl index aae4fb22..c86df617 100644 --- a/hackernews-async-ts/app/view/layout/layout.tpl +++ b/hackernews-tegg/app/view/layout/layout.tpl @@ -4,16 +4,16 @@ - + {% block title %}egg - HackerNews{% endblock %}
{% block content %}{% endblock %} diff --git a/hackernews-async-ts-di/app/view/news/detail.tpl b/hackernews-tegg/app/view/news/detail.tpl similarity index 100% rename from hackernews-async-ts-di/app/view/news/detail.tpl rename to hackernews-tegg/app/view/news/detail.tpl diff --git a/hackernews-async-ts-di/app/view/news/item.tpl b/hackernews-tegg/app/view/news/item.tpl similarity index 100% rename from hackernews-async-ts-di/app/view/news/item.tpl rename to hackernews-tegg/app/view/news/item.tpl diff --git a/hackernews-async-ts-di/app/view/news/list.tpl b/hackernews-tegg/app/view/news/list.tpl similarity index 100% rename from hackernews-async-ts-di/app/view/news/list.tpl rename to hackernews-tegg/app/view/news/list.tpl diff --git a/hackernews-async-ts-di/app/view/news/user.tpl b/hackernews-tegg/app/view/news/user.tpl similarity index 100% rename from hackernews-async-ts-di/app/view/news/user.tpl rename to hackernews-tegg/app/view/news/user.tpl diff --git a/hackernews-tegg/config/config.default.ts b/hackernews-tegg/config/config.default.ts new file mode 100644 index 00000000..575c1482 --- /dev/null +++ b/hackernews-tegg/config/config.default.ts @@ -0,0 +1,35 @@ +import { defineConfigFactory, type EggAppConfig } from 'egg'; + +// for config.{env}.ts +export type DefaultConfig = Partial; + +// app special config scheme +export interface BizConfig { + sourceUrl: string; + news: { + pageSize: number; + serverUrl: string; + }; +} + +export default defineConfigFactory(appInfo => { + return { + // override config from framework / plugin + keys: appInfo.name + '123456', + view: { + defaultViewEngine: 'nunjucks', + mapping: { + '.tpl': 'nunjucks', + }, + }, + siteFile: { + '/favicon.ico': 'https://eggjs.org/favicon.png', + }, + // app special config + sourceUrl: `https://github.com/eggjs/examples/tree/master/${appInfo.name}`, + news: { + pageSize: 30, + serverUrl: 'https://hacker-news.firebaseio.com/v0', + }, + }; +}); diff --git a/hackernews-tegg/config/config.local.ts b/hackernews-tegg/config/config.local.ts new file mode 100644 index 00000000..59a2a094 --- /dev/null +++ b/hackernews-tegg/config/config.local.ts @@ -0,0 +1,7 @@ +import type { DefaultConfig } from './config.default.ts'; + +export default { + news: { + pageSize: 20, + }, +} as DefaultConfig; diff --git a/hackernews-tegg/config/config.prod.ts b/hackernews-tegg/config/config.prod.ts new file mode 100644 index 00000000..03aa9b21 --- /dev/null +++ b/hackernews-tegg/config/config.prod.ts @@ -0,0 +1,7 @@ +import type { DefaultConfig } from './config.default.ts'; + +export default { + news: { + pageSize: 30, + }, +} as DefaultConfig; diff --git a/hackernews-async-ts/config/plugin.ts b/hackernews-tegg/config/plugin.ts similarity index 60% rename from hackernews-async-ts/config/plugin.ts rename to hackernews-tegg/config/plugin.ts index 34d8cfe0..8aafcfdf 100644 --- a/hackernews-async-ts/config/plugin.ts +++ b/hackernews-tegg/config/plugin.ts @@ -1,6 +1,6 @@ export default { nunjucks: { enable: true, - package: 'egg-view-nunjucks', + package: '@eggjs/view-nunjucks', }, }; diff --git a/hackernews-tegg/package.json b/hackernews-tegg/package.json new file mode 100644 index 00000000..816649ba --- /dev/null +++ b/hackernews-tegg/package.json @@ -0,0 +1,45 @@ +{ + "name": "hackernews-tegg", + "version": "1.0.0", + "description": "hackernews showcase using tegg", + "private": true, + "type": "module", + "egg": { + "typescript": true + }, + "scripts": { + "start": "eggctl start", + "stop": "eggctl stop", + "dev": "egg-bin dev", + "test-local": "egg-bin test", + "test": "npm run lint -- --fix && npm run test-local", + "cov": "egg-bin cov", + "tsc": "tsc -p tsconfig.json", + "ci": "npm run lint && npm run cov && npm run tsc", + "lint": "eslint .", + "clean": "tsc -b --clean" + }, + "dependencies": { + "@eggjs/scripts": "beta", + "@eggjs/tegg": "beta", + "@eggjs/tegg-config": "beta", + "@eggjs/tegg-controller-plugin": "beta", + "@eggjs/tegg-plugin": "beta", + "@eggjs/view-nunjucks": "beta", + "egg": "beta", + "moment": "^2.30.1" + }, + "devDependencies": { + "@eggjs/bin": "beta", + "@eggjs/mock": "beta", + "@eggjs/tsconfig": "beta", + "@types/mocha": "10", + "@types/node": "24", + "eslint": "8", + "eslint-config-egg": "14", + "typescript": "5" + }, + "engines": { + "node": ">=22.18.0" + } +} diff --git a/hackernews-async-ts/test/app/controller/news.test.ts b/hackernews-tegg/test/app/controller/news.test.ts similarity index 54% rename from hackernews-async-ts/test/app/controller/news.test.ts rename to hackernews-tegg/test/app/controller/news.test.ts index ff2f9306..802bdbca 100644 --- a/hackernews-async-ts/test/app/controller/news.test.ts +++ b/hackernews-tegg/test/app/controller/news.test.ts @@ -1,19 +1,19 @@ -import { strict as assert } from 'assert'; -import * as cheerio from 'cheerio'; -import { app } from 'egg-mock/bootstrap'; +import assert from 'node:assert/strict'; + +import { app } from '@eggjs/mock/bootstrap'; describe('test/app/controller/news.test.ts', () => { it('should GET /news', async () => { const result = await app.httpRequest().get('/news').expect(200); - const $ = cheerio.load(result.text); - const listItem = $('.news-view .item'); - assert(listItem.length === app.config.news.pageSize); + const m = result.text.match(/.*?<\/span>/g); + assert.ok(m); + assert.equal(m.length, app.config.news.pageSize); }); it('should GET /news/item/:id', async () => { await app.httpRequest() .get('/news/item/1') - // just a example, use regex to test part of dom string, but should be strong characteristic + // just a example, use regex to test part of dom string, but should be strong characteristic .expect(/\/news\/item\/1/) .expect(200); }); @@ -21,7 +21,7 @@ describe('test/app/controller/news.test.ts', () => { it('should GET /news/user/:id', async () => { await app.httpRequest() .get('/news/user/activatedgeek') - // just a example, use regex to test part of dom string, but should be strong characteristic + // just a example, use regex to test part of dom string, but should be strong characteristic .expect(/user:<\/span> activatedgeek/) .expect(200); }); diff --git a/hackernews-async-ts/test/app/service/News.test.ts b/hackernews-tegg/test/app/service/News.test.ts similarity index 63% rename from hackernews-async-ts/test/app/service/News.test.ts rename to hackernews-tegg/test/app/service/News.test.ts index 1d922b04..ada73ba6 100644 --- a/hackernews-async-ts/test/app/service/News.test.ts +++ b/hackernews-tegg/test/app/service/News.test.ts @@ -1,8 +1,9 @@ -import { strict as assert } from 'assert'; +import assert from 'node:assert/strict'; + import { Context } from 'egg'; -import { app } from 'egg-mock/bootstrap'; +import { app } from '@eggjs/mock/bootstrap'; -describe('test/app/service/News.test.js', () => { +describe('test/app/service/News.test.ts', () => { let ctx: Context; before(() => { @@ -11,11 +12,12 @@ describe('test/app/service/News.test.js', () => { it('getTopStories', async () => { const list = await ctx.service.news.getTopStories(); - assert(list.length === 30); + assert.equal(list.length, 30); }); it('getItem', async () => { const item = await ctx.service.news.getItem(1); + console.log(item); assert(item.id && item.title && item.url); }); }); diff --git a/hackernews-async-ts/tsconfig.json b/hackernews-tegg/tsconfig.json similarity index 100% rename from hackernews-async-ts/tsconfig.json rename to hackernews-tegg/tsconfig.json diff --git a/hackernews-tegg/typings/app/controller/index.d.ts b/hackernews-tegg/typings/app/controller/index.d.ts new file mode 100644 index 00000000..d2c84939 --- /dev/null +++ b/hackernews-tegg/typings/app/controller/index.d.ts @@ -0,0 +1,12 @@ +// This file is created by egg-ts-helper@3.2.0 +// Do not modify this file!!!!!!!!! +/* eslint-disable */ + +import 'egg'; +import ExportNews from '../../../app/controller/news.js'; + +declare module 'egg' { + interface IController { + news: ExportNews; + } +} diff --git a/hackernews-tegg/typings/app/index.d.ts b/hackernews-tegg/typings/app/index.d.ts new file mode 100644 index 00000000..d772ae2b --- /dev/null +++ b/hackernews-tegg/typings/app/index.d.ts @@ -0,0 +1,7 @@ +// This file is created by egg-ts-helper@3.2.0 +// Do not modify this file!!!!!!!!! +/* eslint-disable */ + +import 'egg'; +export * from 'egg'; +export as namespace Egg; diff --git a/hackernews-tegg/typings/app/service/index.d.ts b/hackernews-tegg/typings/app/service/index.d.ts new file mode 100644 index 00000000..cacc4624 --- /dev/null +++ b/hackernews-tegg/typings/app/service/index.d.ts @@ -0,0 +1,16 @@ +// This file is created by egg-ts-helper@3.2.0 +// Do not modify this file!!!!!!!!! +/* eslint-disable */ + +import 'egg'; +type AnyClass = new (...args: any[]) => any; +type AnyFunc = (...args: any[]) => T; +type CanExportFunc = AnyFunc> | AnyFunc>; +type AutoInstanceType : T> = U extends AnyClass ? InstanceType : U; +import ExportNews from '../../../app/service/News.js'; + +declare module 'egg' { + interface IService { + news: AutoInstanceType; + } +} diff --git a/hackernews-typescript/.eslintrc b/hackernews-typescript/.eslintrc new file mode 100644 index 00000000..b4ea94ea --- /dev/null +++ b/hackernews-typescript/.eslintrc @@ -0,0 +1,5 @@ +{ + "root": true, + "extends": "eslint-config-egg/typescript" +} + diff --git a/hackernews-typescript/.gitignore b/hackernews-typescript/.gitignore new file mode 100644 index 00000000..59af3eb5 --- /dev/null +++ b/hackernews-typescript/.gitignore @@ -0,0 +1,19 @@ +logs/ +npm-debug.log +node_modules/ +coverage/ +.idea/ +run/ +logs/ +.DS_Store +.vscode +*.swp +*.lock +*.js + +app/**/*.js +test/**/*.js +config/**/*.js +app/**/*.map +test/**/*.map +config/**/*.map \ No newline at end of file diff --git a/hackernews-async-ts/README.md b/hackernews-typescript/README.md similarity index 90% rename from hackernews-async-ts/README.md rename to hackernews-typescript/README.md index 62236e49..8b04b73c 100644 --- a/hackernews-async-ts/README.md +++ b/hackernews-typescript/README.md @@ -1,4 +1,4 @@ -# hackernews-async-ts +# hackernews-typescript [Hacker News](https://news.ycombinator.com/) showcase using typescript && egg @@ -29,5 +29,5 @@ $ npm start ### Requirement -- Node.js 16.x -- Typescript 4.x +- Node.js 22.x +- Typescript 5.x diff --git a/hackernews-async-ts-di/app/controller/news.ts b/hackernews-typescript/app/controller/news.ts similarity index 53% rename from hackernews-async-ts-di/app/controller/news.ts rename to hackernews-typescript/app/controller/news.ts index 16e7f7a2..4a359d49 100644 --- a/hackernews-async-ts-di/app/controller/news.ts +++ b/hackernews-typescript/app/controller/news.ts @@ -1,36 +1,31 @@ import { Controller } from 'egg'; -import { inject } from 'egg-di'; -import { HackerNews } from '../service/HackerNews'; export default class NewsController extends Controller { - @inject() - private readonly hackerNews: HackerNews; - public async list() { const { ctx, app } = this; const pageSize = app.config.news.pageSize; const page = parseInt(ctx.query.page, 10) || 1; - const idList = await this.hackerNews.getTopStories(page); + const idList = await ctx.service.news.getTopStories(page) as number[]; // get itemInfo parallel - const newsList = await Promise.all(idList.map((id) => this.hackerNews.getItem(id))); + const newsList = await Promise.all(idList.map(id => ctx.service.news.getItem(id))); await ctx.render('news/list.tpl', { list: newsList, page, pageSize }); } public async detail() { const { ctx } = this; - const id = ctx.params.id; - const newsInfo = await this.hackerNews.getItem(id); + const id = ctx.params!.id as unknown as number; + const newsInfo = await ctx.service.news.getItem(id); // get comment parallel - const commentList = await Promise.all(newsInfo.kids.map((_id) => this.hackerNews.getItem(_id))); + const commentList = await Promise.all(newsInfo.kids.map(_id => ctx.service.news.getItem(_id))); await ctx.render('news/detail.tpl', { item: newsInfo, comments: commentList }); } public async user() { const { ctx } = this; - const id = ctx.params.id; - const userInfo = await this.hackerNews.getUser(id); + const id = ctx.params!.id as unknown as number; + const userInfo = await ctx.service.news.getUser(id); await ctx.render('news/user.tpl', { user: userInfo }); } } diff --git a/hackernews-typescript/app/extend/filter.ts b/hackernews-typescript/app/extend/filter.ts new file mode 100644 index 00000000..4bd566f6 --- /dev/null +++ b/hackernews-typescript/app/extend/filter.ts @@ -0,0 +1,9 @@ +import moment from 'moment'; + +export function relativeTime(seconds: number) { + return moment(new Date(seconds * 1000)).fromNow(); +} + +export function domain(url: string) { + return url && url.split('/')[2]; +} diff --git a/hackernews-async-ts/app/public/css/news.css b/hackernews-typescript/app/public/css/news.css similarity index 100% rename from hackernews-async-ts/app/public/css/news.css rename to hackernews-typescript/app/public/css/news.css diff --git a/hackernews-typescript/app/router.ts b/hackernews-typescript/app/router.ts new file mode 100644 index 00000000..4b6e8b23 --- /dev/null +++ b/hackernews-typescript/app/router.ts @@ -0,0 +1,10 @@ +import { Application } from 'egg'; + +export default (app: Application) => { + const { controller, router } = app; + + router.redirect('/', '/news', 302); + router.get('/news', controller.news.list); + router.get('/news/item/:id', controller.news.detail); + router.get('/news/user/:id', controller.news.user); +}; diff --git a/hackernews-async-ts-di/app/service/HackerNews.ts b/hackernews-typescript/app/service/News.ts similarity index 66% rename from hackernews-async-ts-di/app/service/HackerNews.ts rename to hackernews-typescript/app/service/News.ts index 30bbfc27..f0663eaa 100644 --- a/hackernews-async-ts-di/app/service/HackerNews.ts +++ b/hackernews-typescript/app/service/News.ts @@ -1,5 +1,4 @@ import { Service } from 'egg'; -import { Context } from 'egg-di'; export interface NewsItem { id: number; @@ -16,24 +15,20 @@ export interface NewsItem { /** * HackerNews Api Service */ -@Context export class HackerNews extends Service { - getConfig() { - return this.app.config.news; - } - /** * request hacker-news api * @param api - Api name * @param opts - urllib options */ - public async request(api: string, opts?: object) { - const options = Object.assign({ + public async request(api: string, opts?: any): Promise { + const url = `${this.config.news.serverUrl}/${api}`; + const result = await this.ctx.httpclient.request(url, { dataType: 'json', - timeout: ['30s', '30s'], - }, opts); - - const result = await this.ctx.curl(`${this.getConfig().serverUrl}/${api}`, options); + timeout: 30000, + ...opts, + }); + this.logger.info('request %s got data %j', url, result.data); return result.data; } @@ -44,17 +39,17 @@ export class HackerNews extends Service { */ public async getTopStories(page?: number, pageSize?: number): Promise { page = page || 1; - pageSize = pageSize || this.getConfig().pageSize; + const requestPageSize = pageSize ?? this.config.news.pageSize; try { const result = await this.request('topstories.json', { data: { orderBy: '"$key"', - startAt: `"${pageSize * (page - 1)}"`, - endAt: `"${pageSize * page - 1}"`, + startAt: `"${requestPageSize * (page - 1)}"`, + endAt: `"${requestPageSize * page - 1}"`, }, }); - return Object.keys(result).map((key) => result[key]); + return Object.keys(result).map(key => result[key]); } catch (e) { this.ctx.logger.error(e); return []; @@ -66,7 +61,7 @@ export class HackerNews extends Service { * @param id - itemId */ public async getItem(id: number): Promise { - return await this.request(`item/${id}.json`); + return await this.request(`item/${id}.json`); } /** diff --git a/hackernews-async-ts-di/app/view/layout/layout.tpl b/hackernews-typescript/app/view/layout/layout.tpl similarity index 73% rename from hackernews-async-ts-di/app/view/layout/layout.tpl rename to hackernews-typescript/app/view/layout/layout.tpl index d9fcc3dc..c86df617 100644 --- a/hackernews-async-ts-di/app/view/layout/layout.tpl +++ b/hackernews-typescript/app/view/layout/layout.tpl @@ -4,16 +4,16 @@ - + {% block title %}egg - HackerNews{% endblock %}
{% block content %}{% endblock %} diff --git a/hackernews-async-ts/app/view/news/detail.tpl b/hackernews-typescript/app/view/news/detail.tpl similarity index 100% rename from hackernews-async-ts/app/view/news/detail.tpl rename to hackernews-typescript/app/view/news/detail.tpl diff --git a/hackernews-async-ts/app/view/news/item.tpl b/hackernews-typescript/app/view/news/item.tpl similarity index 100% rename from hackernews-async-ts/app/view/news/item.tpl rename to hackernews-typescript/app/view/news/item.tpl diff --git a/hackernews-async-ts/app/view/news/list.tpl b/hackernews-typescript/app/view/news/list.tpl similarity index 100% rename from hackernews-async-ts/app/view/news/list.tpl rename to hackernews-typescript/app/view/news/list.tpl diff --git a/hackernews-async-ts/app/view/news/user.tpl b/hackernews-typescript/app/view/news/user.tpl similarity index 100% rename from hackernews-async-ts/app/view/news/user.tpl rename to hackernews-typescript/app/view/news/user.tpl diff --git a/hackernews-typescript/config/config.default.ts b/hackernews-typescript/config/config.default.ts new file mode 100644 index 00000000..575c1482 --- /dev/null +++ b/hackernews-typescript/config/config.default.ts @@ -0,0 +1,35 @@ +import { defineConfigFactory, type EggAppConfig } from 'egg'; + +// for config.{env}.ts +export type DefaultConfig = Partial; + +// app special config scheme +export interface BizConfig { + sourceUrl: string; + news: { + pageSize: number; + serverUrl: string; + }; +} + +export default defineConfigFactory(appInfo => { + return { + // override config from framework / plugin + keys: appInfo.name + '123456', + view: { + defaultViewEngine: 'nunjucks', + mapping: { + '.tpl': 'nunjucks', + }, + }, + siteFile: { + '/favicon.ico': 'https://eggjs.org/favicon.png', + }, + // app special config + sourceUrl: `https://github.com/eggjs/examples/tree/master/${appInfo.name}`, + news: { + pageSize: 30, + serverUrl: 'https://hacker-news.firebaseio.com/v0', + }, + }; +}); diff --git a/hackernews-typescript/config/config.local.ts b/hackernews-typescript/config/config.local.ts new file mode 100644 index 00000000..59a2a094 --- /dev/null +++ b/hackernews-typescript/config/config.local.ts @@ -0,0 +1,7 @@ +import type { DefaultConfig } from './config.default.ts'; + +export default { + news: { + pageSize: 20, + }, +} as DefaultConfig; diff --git a/hackernews-typescript/config/config.prod.ts b/hackernews-typescript/config/config.prod.ts new file mode 100644 index 00000000..03aa9b21 --- /dev/null +++ b/hackernews-typescript/config/config.prod.ts @@ -0,0 +1,7 @@ +import type { DefaultConfig } from './config.default.ts'; + +export default { + news: { + pageSize: 30, + }, +} as DefaultConfig; diff --git a/hackernews-typescript/config/plugin.ts b/hackernews-typescript/config/plugin.ts new file mode 100644 index 00000000..8aafcfdf --- /dev/null +++ b/hackernews-typescript/config/plugin.ts @@ -0,0 +1,6 @@ +export default { + nunjucks: { + enable: true, + package: '@eggjs/view-nunjucks', + }, +}; diff --git a/hackernews-typescript/package.json b/hackernews-typescript/package.json new file mode 100644 index 00000000..11798a53 --- /dev/null +++ b/hackernews-typescript/package.json @@ -0,0 +1,45 @@ +{ + "name": "hackernews-typescript", + "version": "1.0.0", + "description": "hackernews showcase using typescript && egg", + "private": true, + "type": "module", + "egg": { + "typescript": true + }, + "scripts": { + "start": "eggctl start", + "stop": "eggctl stop", + "dev": "egg-bin dev", + "test-local": "egg-bin test", + "test": "npm run lint -- --fix && npm run test-local", + "cov": "egg-bin cov", + "tsc": "tsc -p tsconfig.json", + "ci": "npm run lint && npm run cov && npm run tsc", + "lint": "eslint .", + "clean": "tsc -b --clean" + }, + "dependencies": { + "@eggjs/scripts": "beta", + "@eggjs/tegg": "beta", + "@eggjs/tegg-config": "beta", + "@eggjs/tegg-controller-plugin": "beta", + "@eggjs/tegg-plugin": "beta", + "@eggjs/view-nunjucks": "beta", + "egg": "beta", + "moment": "^2.30.1" + }, + "devDependencies": { + "@eggjs/bin": "beta", + "@eggjs/mock": "beta", + "@eggjs/tsconfig": "beta", + "@types/mocha": "10", + "@types/node": "24", + "eslint": "8", + "eslint-config-egg": "14", + "typescript": "5" + }, + "engines": { + "node": ">=22.18.0" + } +} diff --git a/hackernews-typescript/test/app/controller/news.test.ts b/hackernews-typescript/test/app/controller/news.test.ts new file mode 100644 index 00000000..802bdbca --- /dev/null +++ b/hackernews-typescript/test/app/controller/news.test.ts @@ -0,0 +1,28 @@ +import assert from 'node:assert/strict'; + +import { app } from '@eggjs/mock/bootstrap'; + +describe('test/app/controller/news.test.ts', () => { + it('should GET /news', async () => { + const result = await app.httpRequest().get('/news').expect(200); + const m = result.text.match(/.*?<\/span>/g); + assert.ok(m); + assert.equal(m.length, app.config.news.pageSize); + }); + + it('should GET /news/item/:id', async () => { + await app.httpRequest() + .get('/news/item/1') + // just a example, use regex to test part of dom string, but should be strong characteristic + .expect(/\/news\/item\/1/) + .expect(200); + }); + + it('should GET /news/user/:id', async () => { + await app.httpRequest() + .get('/news/user/activatedgeek') + // just a example, use regex to test part of dom string, but should be strong characteristic + .expect(/user:<\/span> activatedgeek/) + .expect(200); + }); +}); diff --git a/hackernews-typescript/test/app/service/News.test.ts b/hackernews-typescript/test/app/service/News.test.ts new file mode 100644 index 00000000..ada73ba6 --- /dev/null +++ b/hackernews-typescript/test/app/service/News.test.ts @@ -0,0 +1,23 @@ +import assert from 'node:assert/strict'; + +import { Context } from 'egg'; +import { app } from '@eggjs/mock/bootstrap'; + +describe('test/app/service/News.test.ts', () => { + let ctx: Context; + + before(() => { + ctx = app.mockContext(); + }); + + it('getTopStories', async () => { + const list = await ctx.service.news.getTopStories(); + assert.equal(list.length, 30); + }); + + it('getItem', async () => { + const item = await ctx.service.news.getItem(1); + console.log(item); + assert(item.id && item.title && item.url); + }); +}); diff --git a/hackernews-typescript/tsconfig.json b/hackernews-typescript/tsconfig.json new file mode 100644 index 00000000..cff80205 --- /dev/null +++ b/hackernews-typescript/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@eggjs/tsconfig", + "compilerOptions": { + "declaration": false + }, + "exclude": [ + "app/public", + "app/views" + ] +} diff --git a/hackernews-typescript/typings/app/controller/index.d.ts b/hackernews-typescript/typings/app/controller/index.d.ts new file mode 100644 index 00000000..d2c84939 --- /dev/null +++ b/hackernews-typescript/typings/app/controller/index.d.ts @@ -0,0 +1,12 @@ +// This file is created by egg-ts-helper@3.2.0 +// Do not modify this file!!!!!!!!! +/* eslint-disable */ + +import 'egg'; +import ExportNews from '../../../app/controller/news.js'; + +declare module 'egg' { + interface IController { + news: ExportNews; + } +} diff --git a/hackernews-typescript/typings/app/index.d.ts b/hackernews-typescript/typings/app/index.d.ts new file mode 100644 index 00000000..d772ae2b --- /dev/null +++ b/hackernews-typescript/typings/app/index.d.ts @@ -0,0 +1,7 @@ +// This file is created by egg-ts-helper@3.2.0 +// Do not modify this file!!!!!!!!! +/* eslint-disable */ + +import 'egg'; +export * from 'egg'; +export as namespace Egg; diff --git a/hackernews-typescript/typings/app/service/index.d.ts b/hackernews-typescript/typings/app/service/index.d.ts new file mode 100644 index 00000000..cacc4624 --- /dev/null +++ b/hackernews-typescript/typings/app/service/index.d.ts @@ -0,0 +1,16 @@ +// This file is created by egg-ts-helper@3.2.0 +// Do not modify this file!!!!!!!!! +/* eslint-disable */ + +import 'egg'; +type AnyClass = new (...args: any[]) => any; +type AnyFunc = (...args: any[]) => T; +type CanExportFunc = AnyFunc> | AnyFunc>; +type AutoInstanceType : T> = U extends AnyClass ? InstanceType : U; +import ExportNews from '../../../app/service/News.js'; + +declare module 'egg' { + interface IService { + news: AutoInstanceType; + } +}