Skip to content

Commit

Permalink
feat(types): typescript support (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
luckyscript authored and whxaxes committed Mar 22, 2019
1 parent 6be344f commit 33c6b52
Show file tree
Hide file tree
Showing 10 changed files with 275 additions and 1 deletion.
114 changes: 114 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import 'egg';
import { Readable } from 'stream';
interface EggFile {
field: string;
filename: string;
encoding: string;
mime: string;
filepath: string;
}

interface MultipartOptions {
requireFile?: boolean; // required file submit, default is true
defCharset?: string;
limits?: {
fieldNameSize?: number;
fieldSize?: number;
fields?: number;
fileSize?: number;
files?: number;
parts?: number;
headerPairs?: number;
};
checkFile?(
fieldname: string,
file: any,
filename: string,
encoding: string,
mimetype: string
): void | Error;
}

interface MultipartFileStream extends Readable {
fields: any;

filename: string;

fieldname: string;

mime: string;

mimeType: string;

transferEncoding: string;

encoding: string;

truncated: boolean;
}

interface ScheduleOptions {
type?: string;
cron?: string;
cronOptions?: {
tz?: string;
utc?: boolean;
iterator?: boolean;
currentDate?: string|number|Date;
endDate?: string|number|Date;
};
interval?: number|string;
immediate?: boolean;
disable?: boolean;
env?: string[];
}

declare module 'egg' {
interface Context {
/**
* clean up request tmp files helper
* @param {EggFile[]} files file paths need to clenup, default is `ctx.request.files`.
* @return {Promise<void>}
*/
cleanupRequestFiles(files?: EggFile[]): Promise<void>;

/**
* create multipart.parts instance, to get separated files.
* @param {MultipartOptions} options
* @return {Function} return a function which return a Promise
*/
multipart(options?: MultipartOptions): (fn?: Function) => Promise<any>;

/**
* get upload file stream
* @param {MultipartOptions} options
* @return {Promise<MultipartFileStream>}
*/
getFileStream(options?: MultipartOptions): Promise<MultipartFileStream>

}

interface Request {
/**
* Files Object Array
*/
files: EggFile[];
}

interface EggAppConfig {
multipart: {
mode?: string;
autoFields?: boolean;
defaultCharset?: string;
fieldNameSize?: number;
fieldSize?: string|number;
fields?: number;
fileSize?: string|number;
files?: number;
whitelist?: () => string[]|string[];
fileExtensions?: string[];
tmpdir?: string;
cleanSchedule?: ScheduleOptions;
}
}
}
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
"files": [
"app",
"config",
"app.js"
"app.js",
"index.d.ts"
],
"types": "index.d.ts",
"ci": {
"type": "travis, azure-pipelines",
"command": {
Expand All @@ -63,7 +65,9 @@
"uuid": "^3.3.2"
},
"devDependencies": {
"@types/node": "^11.11.3",
"autod": "^3.0.1",
"coffee": "^5.2.1",
"egg": "^2.11.2",
"egg-bin": "^4.9.0",
"egg-ci": "^1.9.2",
Expand All @@ -73,6 +77,7 @@
"formstream": "^1.1.0",
"is-type-of": "^1.0.0",
"urllib": "^2.30.0",
"typescript": "^3.2.2",
"webstorm-disable-index": "^1.2.0"
}
}
1 change: 1 addition & 0 deletions test/fixtures/apps/ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.js
13 changes: 13 additions & 0 deletions test/fixtures/apps/ts/app/controller/home.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Controller } from 'egg';

class HomeController extends Controller {
async index() {
const { ctx } = this;
ctx.body = {
body: ctx.request.body,
files: ctx.request.files,
};
}
}

export default HomeController;
6 changes: 6 additions & 0 deletions test/fixtures/apps/ts/app/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Application } from 'egg';

export default (app: Application) => {
const { controller } = app;
app.post('/', controller.home.index);
}
15 changes: 15 additions & 0 deletions test/fixtures/apps/ts/config/config.default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { EggAppInfo, EggAppConfig, PowerPartial } from 'egg';

export default (appInfo: EggAppInfo) => {
const config = {} as PowerPartial<EggAppConfig>;

config.keys = 'multipart-ts-test';

config.appInfo = appInfo;

config.multipart = {
mode: 'file',
};

return config;
}
3 changes: 3 additions & 0 deletions test/fixtures/apps/ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "multipart-typescript-demo"
}
24 changes: 24 additions & 0 deletions test/fixtures/apps/ts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"compilerOptions": {
"strict": true,
"target": "es2017",
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": false,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"pretty": true,
"noEmitOnError": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"strictPropertyInitialization": false,
"noFallthroughCasesInSwitch": true,
"baseUrl": "./",
"paths": {
"egg-multipart": ["../../../../"]
}
}
}
8 changes: 8 additions & 0 deletions test/fixtures/apps/ts/typings/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'egg';
import HomeController from '../app/controller/home';

declare module 'egg' {
interface IController {
home: HomeController;
}
}
85 changes: 85 additions & 0 deletions test/ts.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict';

const assert = require('assert');
const formstream = require('formstream');
const urllib = require('urllib');
const path = require('path');
const mock = require('egg-mock');
const rimraf = require('mz-modules/rimraf');
const coffee = require('coffee');

describe('test/ts.test.js', () => {
it('should compile ts without err', () => {
return coffee.fork(
require.resolve('typescript/bin/tsc'),
[ '-p', path.resolve(__dirname, './fixtures/apps/ts/tsconfig.json') ]
)
.debug()
.expect('code', 0)
.end();
});
});

describe('test/ts.test.js', () => {
let app;
let server;
let host;
before(() => {
app = mock.app({
baseDir: 'apps/ts',
});
return app.ready();
});
before(() => {
server = app.listen();
host = 'http://127.0.0.1:' + server.address().port;
});
after(() => {
return rimraf(app.config.multipart.tmpdir);
});
after(() => app.close());
after(() => server.close());
beforeEach(() => app.mockCsrf());
afterEach(mock.restore);

it('ts should run without err', async () => {
const form = formstream();
form.field('foo', 'bar').field('luckyscript', 'egg');
form.file('file1', __filename, 'foooooooo.js');
form.file('file2', __filename);
// will ignore empty file
form.buffer('file3', Buffer.from(''), '', 'application/octet-stream');
form.file('bigfile', path.join(__dirname, 'fixtures', 'bigfile.js'));
// other form fields
form.field('work', 'with Node.js');

const headers = form.headers();
const res = await urllib.request(host + '/', {
method: 'POST',
headers,
stream: form,
});

assert(res.status === 200);
const data = JSON.parse(res.data);
assert.deepStrictEqual(data.body, { foo: 'bar', luckyscript: 'egg', work: 'with Node.js' });
assert(data.files.length === 3);
assert(data.files[0].field === 'file1');
assert(data.files[0].filename === 'foooooooo.js');
assert(data.files[0].encoding === '7bit');
assert(data.files[0].mime === 'application/javascript');
assert(data.files[0].filepath.startsWith(app.config.multipart.tmpdir));

assert(data.files[1].field === 'file2');
assert(data.files[1].filename === 'ts.test.js');
assert(data.files[1].encoding === '7bit');
assert(data.files[1].mime === 'application/javascript');
assert(data.files[1].filepath.startsWith(app.config.multipart.tmpdir));

assert(data.files[2].field === 'bigfile');
assert(data.files[2].filename === 'bigfile.js');
assert(data.files[2].encoding === '7bit');
assert(data.files[2].mime === 'application/javascript');
assert(data.files[2].filepath.startsWith(app.config.multipart.tmpdir));
});
});

0 comments on commit 33c6b52

Please sign in to comment.